<!-- build time:Tue Feb 18 2020 17:19:27 GMT+0800 (China Standard Time) --><!DOCTYPE html><html lang="zh"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no,minimal-ui"><meta name="renderer" content="webkit"><meta http-equiv="Cache-Control" content="no-transform"><meta http-equiv="Cache-Control" content="no-siteapp"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black"><meta name="format-detection" content="telephone=no,email=no,adress=no"><meta name="theme-color" content="#000000"><meta http-equiv="window-target" content="_top"><title>Spring Boot 多数据源（读写分离）入门 | calm&#39;s blog</title><meta name="description" content="摘要: 原创出处 http:&#x2F;&#x2F;www.iocoder.cn&#x2F;Spring-Boot&#x2F;dynamic-datasource&#x2F; 「芋道源码」，卡姆在此基础上略作修改，欢迎转载，保留摘要，谢谢！1. 概述在项目中，我们可能会碰到需要多数据源的场景。例如说：读写分离：数据库主节点压力比较大，需要增加从节点提供读操作，以减少压力。多数据源：一个复杂的单体项目，因为没有拆分成不同的服务，需要连接多个业务的数"><meta property="og:type" content="article"><meta property="og:title" content="Spring Boot 多数据源（读写分离）入门"><meta property="og:url" content="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/index.html"><meta property="og:site_name" content="卡姆"><meta property="og:description" content="摘要: 原创出处 http:&#x2F;&#x2F;www.iocoder.cn&#x2F;Spring-Boot&#x2F;dynamic-datasource&#x2F; 「芋道源码」，卡姆在此基础上略作修改，欢迎转载，保留摘要，谢谢！1. 概述在项目中，我们可能会碰到需要多数据源的场景。例如说：读写分离：数据库主节点压力比较大，需要增加从节点提供读操作，以减少压力。多数据源：一个复杂的单体项目，因为没有拆分成不同的服务，需要连接多个业务的数"><meta property="og:locale" content="zh_CN"><meta property="article:published_time" content="2020-02-13T03:18:37.000Z"><meta property="article:modified_time" content="2020-02-17T08:16:40.827Z"><meta property="article:author" content="calm"><meta property="article:tag" content="多数据源"><meta name="twitter:card" content="summary"><link rel="canonical" href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/index.html"><link rel="alternate" href="/atom.xml" title="卡姆" type="application/atom+xml"><link rel="icon" href="/blog/images/avatar.jpg" type="image/x-icon"><link rel="stylesheet" href="/blog/css/style.css"><link rel="stylesheet" href="//cdn.jsdelivr.net/npm/gitalk@1.4.0/dist/gitalk.min.css"><meta name="generator" content="Hexo 4.2.0"></head><body class="main-center theme-green" itemscope itemtype="http://schema.org/WebPage"><header class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="slimContent"><div class="navbar-header"><div class="profile-block text-center"><a id="avatar" href="https://github.com/yuyong725" target="_blank"><img class="img-circle img-rotate" src="/blog/images/avatar.jpg" width="200" height="200"></a><h2 id="name" class="hidden-xs hidden-sm">卡姆</h2><h3 id="title" class="hidden-xs hidden-sm hidden-md">a Java coder need calm</h3><small id="location" class="text-muted hidden-xs hidden-sm"><i class="icon icon-map-marker"></i> hangzhou, China</small></div><div class="search" id="search-form-wrap"><form class="search-form sidebar-form"><div class="input-group"><input type="text" class="search-form-input form-control" placeholder="搜索"> <span class="input-group-btn"><button type="submit" class="search-form-submit btn btn-flat" onclick="return!1"><i class="icon icon-search"></i></button></span></div></form><div class="ins-search"><div class="ins-search-mask"></div><div class="ins-search-container"><div class="ins-input-wrapper"><input type="text" class="ins-search-input" placeholder="想要查找什么..." x-webkit-speech> <button type="button" class="close ins-close ins-selectable" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button></div><div class="ins-section-wrapper"><div class="ins-section-container"></div></div></div></div></div><button class="navbar-toggle collapsed" type="button" data-toggle="collapse" data-target="#main-navbar" aria-controls="main-navbar" aria-expanded="false"><span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span></button></div><nav id="main-navbar" class="collapse navbar-collapse" itemscope itemtype="http://schema.org/SiteNavigationElement" role="navigation"><ul class="nav navbar-nav main-nav"><li class="menu-item menu-item-home"><a href="/blog/."><i class="icon icon-home-fill"></i> <span class="menu-title">首页</span></a></li><li class="menu-item menu-item-archives"><a href="/blog/archives"><i class="icon icon-archives-fill"></i> <span class="menu-title">归档</span></a></li><li class="menu-item menu-item-categories"><a href="/blog/categories"><i class="icon icon-folder"></i> <span class="menu-title">分类</span></a></li><li class="menu-item menu-item-tags"><a href="/blog/tags"><i class="icon icon-tags"></i> <span class="menu-title">标签</span></a></li><li class="menu-item menu-item-repository"><a href="/blog/repository"><i class="icon icon-project"></i> <span class="menu-title">项目</span></a></li><li class="menu-item menu-item-books"><a href="/blog/books"><i class="icon icon-book-fill"></i> <span class="menu-title">书单</span></a></li><li class="menu-item menu-item-links"><a href="/blog/links"><i class="icon icon-friendship"></i> <span class="menu-title">友链</span></a></li><li class="menu-item menu-item-about"><a href="/blog/about"><i class="icon icon-cup-fill"></i> <span class="menu-title">关于</span></a></li></ul><ul class="social-links"><li><a href="https://github.com/yuyong725" target="_blank" title="Github" data-toggle="tooltip" data-placement="top"><i class="icon icon-github"></i></a></li></ul></nav></div></header><aside class="sidebar" itemscope itemtype="http://schema.org/WPSideBar"><div class="slimContent"><div class="widget"><h3 class="widget-title">公告</h3><div class="widget-body"><div id="board"><div class="content"><p>欢迎交流与分享经验!</p></div></div></div></div><div class="widget"><h3 class="widget-title">分类</h3><div class="widget-body"><ul class="category-list"><li class="category-list-item"><a class="category-list-link" href="/blog/categories/CI-DI/">CI/DI</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/blog/categories/git/">git</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/blog/categories/labs/">labs</a><span class="category-list-count">14</span></li></ul></div></div><div class="widget"><h3 class="widget-title">标签</h3><div class="widget-body"><ul class="tag-list" itemprop="keywords"><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/MyBatis/" rel="tag">MyBatis</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/Oauth2/" rel="tag">Oauth2</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/Spring-Security/" rel="tag">Spring Security</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/SpringMVC/" rel="tag">SpringMVC</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/docker/" rel="tag">docker</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/dubbo/" rel="tag">dubbo</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/git/" rel="tag">git</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/jdbctemplate/" rel="tag">jdbctemplate</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/jerkins/" rel="tag">jerkins</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/job/" rel="tag">job</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/jpa/" rel="tag">jpa</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/pdf/" rel="tag">pdf</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/redis/" rel="tag">redis</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/%E5%88%86%E5%BA%93%E5%88%86%E8%A1%A8/" rel="tag">分库分表</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/%E5%A4%9A%E6%95%B0%E6%8D%AE%E6%BA%90/" rel="tag">多数据源</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/%E6%95%B0%E6%8D%AE%E5%BA%93%E7%89%88%E6%9C%AC%E7%AE%A1%E7%90%86/" rel="tag">数据库版本管理</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BF%9E%E6%8E%A5%E6%B1%A0/" rel="tag">数据库连接池</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/%E7%BC%93%E5%AD%98/" rel="tag">缓存</a><span class="tag-list-count">2</span></li></ul></div></div><div class="widget"><h3 class="widget-title">标签云</h3><div class="widget-body tagcloud"><a href="/blog/tags/MyBatis/" style="font-size:13px">MyBatis</a> <a href="/blog/tags/Oauth2/" style="font-size:13px">Oauth2</a> <a href="/blog/tags/Spring-Security/" style="font-size:13px">Spring Security</a> <a href="/blog/tags/SpringMVC/" style="font-size:13px">SpringMVC</a> <a href="/blog/tags/docker/" style="font-size:13px">docker</a> <a href="/blog/tags/dubbo/" style="font-size:13px">dubbo</a> <a href="/blog/tags/git/" style="font-size:13px">git</a> <a href="/blog/tags/jdbctemplate/" style="font-size:13px">jdbctemplate</a> <a href="/blog/tags/jerkins/" style="font-size:13px">jerkins</a> <a href="/blog/tags/job/" style="font-size:14px">job</a> <a href="/blog/tags/jpa/" style="font-size:13px">jpa</a> <a href="/blog/tags/pdf/" style="font-size:13px">pdf</a> <a href="/blog/tags/redis/" style="font-size:13px">redis</a> <a href="/blog/tags/%E5%88%86%E5%BA%93%E5%88%86%E8%A1%A8/" style="font-size:13px">分库分表</a> <a href="/blog/tags/%E5%A4%9A%E6%95%B0%E6%8D%AE%E6%BA%90/" style="font-size:13px">多数据源</a> <a href="/blog/tags/%E6%95%B0%E6%8D%AE%E5%BA%93%E7%89%88%E6%9C%AC%E7%AE%A1%E7%90%86/" style="font-size:13px">数据库版本管理</a> <a href="/blog/tags/%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BF%9E%E6%8E%A5%E6%B1%A0/" style="font-size:13px">数据库连接池</a> <a href="/blog/tags/%E7%BC%93%E5%AD%98/" style="font-size:14px">缓存</a></div></div><div class="widget"><h3 class="widget-title">归档</h3><div class="widget-body"><ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/blog/archives/2020/02/">二月 2020</a><span class="archive-list-count">5</span></li><li class="archive-list-item"><a class="archive-list-link" href="/blog/archives/2020/01/">一月 2020</a><span class="archive-list-count">8</span></li><li class="archive-list-item"><a class="archive-list-link" href="/blog/archives/2019/12/">十二月 2019</a><span class="archive-list-count">4</span></li></ul></div></div><div class="widget"><h3 class="widget-title">最新文章</h3><div class="widget-body"><ul class="recent-post-list list-unstyled no-thumbnail"><li><div class="item-inner"><p class="item-category"><a class="category-link" href="/blog/categories/labs/">labs</a></p><p class="item-title"><a href="/blog/2020/02/18/labs-springboot2-async-Job/" class="title">Spring Boot 异步任务入门</a></p><p class="item-date"><time datetime="2020-02-18T05:10:39.000Z" itemprop="datePublished">2020-02-18</time></p></div></li><li><div class="item-inner"><p class="item-category"><a class="category-link" href="/blog/categories/labs/">labs</a></p><p class="item-title"><a href="/blog/2020/02/18/labs-springboot2-job/" class="title">Spring Boot 定时任务入门</a></p><p class="item-date"><time datetime="2020-02-18T02:26:31.000Z" itemprop="datePublished">2020-02-18</time></p></div></li><li><div class="item-inner"><p class="item-category"><a class="category-link" href="/blog/categories/labs/">labs</a></p><p class="item-title"><a href="/blog/2020/02/17/labs-springboot2-database-version-control/" class="title">Spring Boot 数据库版本管理入门</a></p><p class="item-date"><time datetime="2020-02-17T12:05:54.000Z" itemprop="datePublished">2020-02-17</time></p></div></li><li><div class="item-inner"><p class="item-category"><a class="category-link" href="/blog/categories/labs/">labs</a></p><p class="item-title"><a href="/blog/2020/02/17/labs-springboot2-sharding-datasource/" class="title">Spring Boot 分库分表入门</a></p><p class="item-date"><time datetime="2020-02-17T08:15:31.000Z" itemprop="datePublished">2020-02-17</time></p></div></li><li><div class="item-inner"><p class="item-category"><a class="category-link" href="/blog/categories/labs/">labs</a></p><p class="item-title"><a href="/blog/2020/02/13/labs-springboot2-dynamic-datasource/" class="title">Spring Boot 多数据源（读写分离）入门</a></p><p class="item-date"><time datetime="2020-02-13T03:18:37.000Z" itemprop="datePublished">2020-02-13</time></p></div></li></ul></div></div></div></aside><aside class="sidebar sidebar-toc collapse in" id="collapseToc" itemscope itemtype="http://schema.org/WPSideBar"><div class="slimContent"><nav id="toc" class="article-toc"><h3 class="toc-title">文章目录</h3><ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#1-概述"><span class="toc-text">1. 概述</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#2-实现方式"><span class="toc-text">2. 实现方式</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#2-1-方案一"><span class="toc-text">2.1 方案一</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-2-方案二"><span class="toc-text">2.2 方案二</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-3-方案三"><span class="toc-text">2.3 方案三</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#3-baomidou-多数据源"><span class="toc-text">3. baomidou 多数据源</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#3-1-引入依赖"><span class="toc-text">3.1 引入依赖</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-2-BaomidouApplication"><span class="toc-text">3.2 BaomidouApplication</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-3-应用配置文件"><span class="toc-text">3.3 应用配置文件</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-4-MyBatis-配置文件"><span class="toc-text">3.4 MyBatis 配置文件</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-5-实体类"><span class="toc-text">3.5 实体类</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-6-Mapper"><span class="toc-text">3.6 Mapper</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-7-简单测试"><span class="toc-text">3.7 简单测试</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-8-详细测试"><span class="toc-text">3.8 详细测试</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#4-baomidou-读写分离"><span class="toc-text">4. baomidou 读写分离</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#4-1-引入依赖"><span class="toc-text">4.1 引入依赖</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#4-2-BaomidouApplication"><span class="toc-text">4.2 BaomidouApplication</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#4-3-应用配置文件"><span class="toc-text">4.3 应用配置文件</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#4-4-MyBatis-配置文件"><span class="toc-text">4.4 MyBatis 配置文件</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#4-5-OrderDO"><span class="toc-text">4.5 OrderDO</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#4-6-OrderMapper"><span class="toc-text">4.6 OrderMapper</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-7-简单测试-1"><span class="toc-text">3.7 简单测试</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-8-详细测试-1"><span class="toc-text">3.8 详细测试</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#5-MyBatis-多数据源"><span class="toc-text">5. MyBatis 多数据源</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#5-1-引入依赖"><span class="toc-text">5.1 引入依赖</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#5-2-Application"><span class="toc-text">5.2 Application</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#5-3-应用配置文件"><span class="toc-text">5.3 应用配置文件</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#5-4-MyBatis-配置文件"><span class="toc-text">5.4 MyBatis 配置文件</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#5-5-MyBatis-配置类"><span class="toc-text">5.5 MyBatis 配置类</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#5-6-实体类"><span class="toc-text">5.6 实体类</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#5-7-Mapper"><span class="toc-text">5.7 Mapper</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#5-8-简单测试"><span class="toc-text">5.8 简单测试</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#5-9-详细测试"><span class="toc-text">5.9 详细测试</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#5-10-读写分离"><span class="toc-text">5.10 读写分离</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#6-Spring-Data-JPA-多数据源"><span class="toc-text">6. Spring Data JPA 多数据源</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#6-1-引入依赖"><span class="toc-text">6.1 引入依赖</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#6-2-Application"><span class="toc-text">6.2 Application</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#6-3-应用配置文件"><span class="toc-text">6.3 应用配置文件</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#6-4-Spring-Data-JPA-配置类"><span class="toc-text">6.4 Spring Data JPA 配置类</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#6-5-实体类"><span class="toc-text">6.5 实体类</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#6-6-Repository"><span class="toc-text">6.6 Repository</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#6-7-简单测试"><span class="toc-text">6.7 简单测试</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#6-8-详细测试"><span class="toc-text">6.8 详细测试</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#6-9-读写分离"><span class="toc-text">6.9 读写分离</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#7-JdbcTemplate-多数据源"><span class="toc-text">7. JdbcTemplate 多数据源</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#7-1-引入依赖"><span class="toc-text">7.1 引入依赖</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#7-2-Application"><span class="toc-text">7.2 Application</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#7-3-应用配置文件"><span class="toc-text">7.3 应用配置文件</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#7-4-JdbcTemplate-配置类"><span class="toc-text">7.4 JdbcTemplate 配置类</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#7-5-实体类"><span class="toc-text">7.5 实体类</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#7-6-Dao"><span class="toc-text">7.6 Dao</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#7-7-简单测试"><span class="toc-text">7.7 简单测试</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#7-8-详细测试"><span class="toc-text">7.8 详细测试</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#7-9-读写分离"><span class="toc-text">7.9 读写分离</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#8-Sharding-JDBC-多数据源"><span class="toc-text">8. Sharding-JDBC 多数据源</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#8-1-引入依赖"><span class="toc-text">8.1 引入依赖</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#8-2-Application"><span class="toc-text">8.2 Application</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#8-3-应用配置文件"><span class="toc-text">8.3 应用配置文件</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#8-4-MyBatis-配置文件"><span class="toc-text">8.4 MyBatis 配置文件</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#8-5-实体类"><span class="toc-text">8.5 实体类</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#8-6-Mapper"><span class="toc-text">8.6 Mapper</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#8-7-简单测试"><span class="toc-text">8.7 简单测试</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#8-8-详细测试"><span class="toc-text">8.8 详细测试</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#9-Sharding-JDBC-读写分离"><span class="toc-text">9. Sharding-JDBC 读写分离</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#9-1-引入依赖"><span class="toc-text">9.1 引入依赖</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#9-2-Application"><span class="toc-text">9.2 Application</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#9-3-应用配置文件"><span class="toc-text">9.3 应用配置文件</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#9-4-MyBatis-配置文件"><span class="toc-text">9.4 MyBatis 配置文件</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#9-5-OrderDO"><span class="toc-text">9.5 OrderDO</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#9-6-OrderMapper"><span class="toc-text">9.6 OrderMapper</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#9-7-简单测试"><span class="toc-text">9.7 简单测试</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#9-8-详细测试"><span class="toc-text">9.8 详细测试</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#666-彩蛋"><span class="toc-text">666. 彩蛋</span></a></li></ol></nav></div></aside><main class="main" role="main"><div class="content"><article id="post-labs-springboot2-dynamic-datasource" class="article article-type-post" itemscope itemtype="http://schema.org/BlogPosting"><div class="article-header"><h1 class="article-title" itemprop="name">Spring Boot 多数据源（读写分离）入门</h1><div class="article-meta"><span class="article-date"><i class="icon icon-calendar-check"></i> <a href="/blog/2020/02/13/labs-springboot2-dynamic-datasource/" class="article-date"><time datetime="2020-02-13T03:18:37.000Z" itemprop="datePublished">2020-02-13</time></a></span> <span class="article-category"><i class="icon icon-folder"></i> <a class="article-category-link" href="/blog/categories/labs/">labs</a></span> <span class="article-tag"><i class="icon icon-tags"></i> <a class="article-tag-link" href="/blog/tags/%E5%A4%9A%E6%95%B0%E6%8D%AE%E6%BA%90/" rel="tag">多数据源</a></span> <span class="article-read hidden-xs"><i class="icon icon-eye-fill" aria-hidden="true"></i> <span id="busuanzi_container_page_pv"><span id="busuanzi_value_page_pv">0</span></span></span> <span class="post-comment"><i class="icon icon-comment"></i> <a href="/blog/2020/02/13/labs-springboot2-dynamic-datasource/#comments" class="article-comment-link">评论</a></span> <span class="post-wordcount hidden-xs" itemprop="wordCount">字数统计: 14.9k(字)</span> <span class="post-readcount hidden-xs" itemprop="timeRequired">阅读时长: 65(分)</span></div></div><div class="article-entry marked-body" itemprop="articleBody"><p>摘要: 原创出处 <a href="http://www.iocoder.cn/Spring-Boot/dynamic-datasource/" target="_blank" rel="noopener">http://www.iocoder.cn/Spring-Boot/dynamic-datasource/</a> 「芋道源码」，卡姆在此基础上略作修改，欢迎转载，保留摘要，谢谢！</p><h1 id="1-概述"><a href="#1-概述" class="headerlink" title="1. 概述"></a>1. 概述</h1><p>在项目中，我们可能会碰到需要多数据源的场景。例如说：</p><ul><li>读写分离：数据库主节点压力比较大，需要增加从节点提供读操作，以减少压力。</li><li>多数据源：一个复杂的单体项目，因为没有拆分成不同的服务，需要连接<strong>多个业务</strong>的数据源。</li></ul><p>本质上，读写分离，仅仅是多数据源的一个场景，从节点是只提供读操作的数据源。所以只要实现了多数据源的功能，也就能够提供读写分离。</p><h1 id="2-实现方式"><a href="#2-实现方式" class="headerlink" title="2. 实现方式"></a>2. 实现方式</h1><p>目前，实现多数据源有三种方案。我们逐个小节来看。</p><h2 id="2-1-方案一"><a href="#2-1-方案一" class="headerlink" title="2.1 方案一"></a>2.1 方案一</h2><p><strong>基于 Spring <a href="https://github.com/spring-projects/spring-framework/blob/master/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/lookup/AbstractRoutingDataSource.java" target="_blank" rel="noopener">AbstractRoutingDataSource</a> 做拓展</strong>。</p><p>简单来说，通过继承 AbstractRoutingDataSource 抽象类，实现一个管理项目中多个 DataSource 的<strong>动态</strong> DynamicRoutingDataSource 实现类。这样，Spring 在获取数据源时，可以通过 DynamicRoutingDataSource 返回<strong>实际</strong>的 DataSource 。</p><p>然后，我们可以自定义一个 <code>@DS</code> 注解，可以添加在 Service 方法、Dao 方法上，表示其<strong>实际</strong>对应的 DataSource 。</p><p>如此，整个过程就变成，执行数据操作时，通过“配置”的 <code>@DS</code> 注解，使用 DynamicRoutingDataSource 获得<strong>对应的实际的</strong> DataSource 。之后，在通过该 DataSource 获得 Connection 连接，最后发起数据库操作。</p><p>可能这么说，没有实现过多数据源的胖友会比较懵逼，比较大概率。所以推荐胖胖看看「芋道源码」的基友写的 <a href="http://www.iocoder.cn/Fight/Profiling-Spring-multiple-data-sources" target="_blank" rel="noopener">《剖析 Spring 多数据源》</a> 文章。</p><p>不过呢，这种方式在结合 Spring 事务的时候，会存在无法切换数据源的问题。具体我们在 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「3. baomidou 多数据源」</a> 中，结合示例一起来看。</p><p>「芋道源码」目前找了一圈开源的项目，发现比较好的是 baomidou 提供的 <a href="https://mp.baomidou.com/guide/dynamic-datasource.html" target="_blank" rel="noopener">dynamic-datasource-spring-boot-starter</a> 。所以我们在 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「3. baomidou 多数据源」</a> 和 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「4. baomidou 读写分离」</a> 中，会使用到它。</p><h2 id="2-2-方案二"><a href="#2-2-方案二" class="headerlink" title="2.2 方案二"></a>2.2 方案二</h2><p><strong>不同操作类，固定数据源</strong>。</p><p>关于这个方案，解释起来略有点晦涩。以 MyBatis 举例子，假设有 <code>orders</code> 和 <code>users</code> 两个数据源。 那么我们可以创建两个 SqlSessionTemplate <code>ordersSqlSessionTemplate</code> 和 <code>usersSqlSessionTemplate</code> ，分别使用这两个数据源。</p><p>然后，配置不同的 Mapper 使用不同的 SqlSessionTemplate 。</p><p>如此，整个过程就变成，执行数据操作时，通过 Mapper 可以对应到其 SqlSessionTemplate ，使用 SqlSessionTemplate 获得<strong>对应的实际的</strong> DataSource 。之后，在通过该 DataSource 获得 Connection 连接，最后发起数据库操作。</p><p>咳咳咳，是不是又处于懵逼状态了？！没事，咱在 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「5. MyBatis 多数据源」</a>、<a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「6. Spring Data JPA 多数据源」</a>、<a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「7. JdbcTemplate 多数据源」</a> 中，结合案例一起看。「Talk is cheap. Show me the code」</p><p>不过呢，这种方式在结合 Spring 事务的时候，也会存在无法切换数据源的问题。淡定淡定。多数据源的情况下，这个基本是逃不掉的问题。</p><h2 id="2-3-方案三"><a href="#2-3-方案三" class="headerlink" title="2.3 方案三"></a>2.3 方案三</h2><p><strong>分库分表中间件</strong>。</p><p>对于分库分表的中间件，会解析我们编写的 SQL ，路由操作到对应的数据源。那么，它们天然就支持多数据源。如此，我们仅需配置好每个表对应的数据源，中间件就可以<strong>透明</strong>的实现多数据源或者读写分离。</p><p>目前，Java 最好用的分库分表中间件，就是 <a href="https://sharding-jdbc.apache.org/" target="_blank" rel="noopener">Apache ShardingSphere</a> ，没有之一。</p><p>那么，这种方式在结合 Spring 事务的时候，会不会存在无法切换数据源的问题呢？答案是<strong>不会</strong>。在上述的方案一和方案二中，在 Spring 事务中，会获得对应的 DataSource ，再获得 Connection 进行数据库操作。而获得的 Connection 以及其上的事务，会通过 ThreadLocal 的方式和当前线程进行绑定。这样，就导致我们无法切换数据源。</p><p>难道分库分表中间件不也是需要 Connection 进行这些事情么？答案是的，但是不同的是分库分表中间件返回的 Connection 返回的实际是<strong>动态</strong>的 DynamicRoutingConnection ，它管理了整个请求（逻辑）过程中，使用的所有的 Connection ，而最终执行 SQL 的时候，DynamicRoutingConnection 会解析 SQL ，获得表对应的真正的 Connection 执行 SQL 操作。</p><p>难道方案一和方案二不可以这么做吗？答案是，当然可以。前提是，他们要实现解析 SQL 的能力。</p><p>那么，分库分表中间件就是多数据源的完美方案落？从一定程度上来说，是的。但是，它需要解决多个 Connection 可能产生的多个事务的一致性问题，也就是我们常说的，分布式事务。关于这块，「芋道源码」最近有段时间没跟进 Sharding-JDBC 的版本，所以无法给出肯定的答案。不过我相信，Sharding-JDBC 最终会解决分布式事务的难题，提供<strong>透明</strong>的多数据源的功能。</p><p>在 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「8. Sharding-JDBC 多数据源」</a>、<a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「9. Sharding-JDBC 读写分离」</a> 中，我们会演示这种方案。</p><h1 id="3-baomidou-多数据源"><a href="#3-baomidou-多数据源" class="headerlink" title="3. baomidou 多数据源"></a>3. baomidou 多数据源</h1><blockquote><p>示例代码对应仓库：<a href="https://github.com/yuyong725/labs-springboot2/tree/master/labs-springboot2-data-access/labs-springboot2-datasource-dynamic/labs-springboot2-datasource-dynamic-baomidou-01" target="_blank" rel="noopener">labs-springboot2-datasource-dynamic-baomidou-01</a> 。</p></blockquote><p>本小节，我们使用实现开源项目 <a href="https://mp.baomidou.com/guide/dynamic-datasource.html" target="_blank" rel="noopener">dynamic-datasource-spring-boot-starter</a> ，来实现多数据源的功能。我们会使用 <code>test_orders</code> 和 <code>test_users</code> 两个数据源作为两个数据源，然后实现在其上的 SQL 操作。并且，会结合在 Spring 事务的不同场景下，会发生的结果以及原因。</p><p>另外，关于 <code>dynamic-datasource-spring-boot-starter</code> 的介绍，胖友自己看 <a href="https://mp.baomidou.com/guide/dynamic-datasource.html" target="_blank" rel="noopener">官方文档</a> 。😈它和 MyBatis-Plus 都是开发者 <a href="https://github.com/baomidou" target="_blank" rel="noopener">baomidou</a> 提供的。</p><h2 id="3-1-引入依赖"><a href="#3-1-引入依赖" class="headerlink" title="3.1 引入依赖"></a>3.1 引入依赖</h2><p>在 <code>pom.xml</code>文件中，引入相关依赖。</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">project</span> <span class="attr">xmlns</span>=<span class="string">"http://maven.apache.org/POM/4.0.0"</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xmlns:xsi</span>=<span class="string">"http://www.w3.org/2001/XMLSchema-instance"</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xsi:schemaLocation</span>=<span class="string">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>labs-springboot2-datasource-dynamic<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>cn.javadog<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.0-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">modelVersion</span>&gt;</span>4.0.0<span class="tag">&lt;/<span class="name">modelVersion</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>labs-springboot2-datasource-dynamic-baomidou-01<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 实现对数据库连接池的自动化配置 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-jdbc<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 本示例，我们使用 MySQL --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>mysql<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>mysql-connector-java<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 实现对 MyBatis 的自动化配置 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.mybatis.spring.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>mybatis-spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 实现对 dynamic-datasource 的自动化配置 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.baomidou<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>dynamic-datasource-spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.5.7<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 不造为啥 dynamic-datasource-spring-boot-starter 会依赖这个 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-actuator<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 方便等会写单元测试 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-test<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">scope</span>&gt;</span>test<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br></pre></td></tr></table></figure><p>具体每个依赖的作用，胖友自己认真看下添加的所有注释噢。</p><h2 id="3-2-BaomidouApplication"><a href="#3-2-BaomidouApplication" class="headerlink" title="3.2 BaomidouApplication"></a>3.2 BaomidouApplication</h2><p>创建 <code>BaomidouApplication.java</code>类，代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// BaomidouApplication.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@MapperScan</span>(basePackages = <span class="string">"cn.javadog.labs.springboot2.datasource.dynamic.baomidou.one.mapper"</span>)</span><br><span class="line"><span class="meta">@EnableAspectJAutoProxy</span>(exposeProxy = <span class="keyword">true</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BaomidouApplication</span> </span>&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>添加 <code>@MapperScan</code> 注解，<code>cn.javadog.labs.springboot2.datasource.dynamic.baomidou.one.mapper</code> 包路径下，就是我们 Mapper 接口所在的包路径。</li><li>添加 <code>@EnableAspectJAutoProxy</code> 注解，重点是配置 <code>exposeProxy = true</code> ，因为我们希望 Spring AOP 能将当前代理对象设置到 <a href="https://github.com/spring-projects/spring-framework/blob/master/spring-aop/src/main/java/org/springframework/aop/framework/AopContext.java" target="_blank" rel="noopener">AopContext</a> 中。具体用途，我们会在下文看到。想要提前看的胖友，可以看看 <a href="http://www.voidcn.com/article/p-zddcuyii-bpt.html" target="_blank" rel="noopener">《Spring AOP 通过获取代理对象实现事务切换》</a> 文章。</li></ul><h2 id="3-3-应用配置文件"><a href="#3-3-应用配置文件" class="headerlink" title="3.3 应用配置文件"></a>3.3 应用配置文件</h2><p>在 <code>resources</code> 目录下，创建 <code>application.yaml</code> 配置文件。配置如下：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">datasource:</span></span><br><span class="line">    <span class="comment"># dynamic-datasource-spring-boot-starter 动态数据源的配置内容</span></span><br><span class="line">    <span class="attr">dynamic:</span></span><br><span class="line">      <span class="attr">primary:</span> <span class="string">users</span> <span class="comment"># 设置默认的数据源或者数据源组，默认值即为 master</span></span><br><span class="line">      <span class="attr">datasource:</span></span><br><span class="line">        <span class="comment"># 订单 orders 数据源配置</span></span><br><span class="line">        <span class="attr">orders:</span></span><br><span class="line">          <span class="attr">url:</span> <span class="string">jdbc:mysql://127.0.0.1:3306/labs-springboot2-multiple?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai</span></span><br><span class="line">          <span class="attr">driver-class-name:</span> <span class="string">com.mysql.cj.jdbc.Driver</span></span><br><span class="line">          <span class="attr">username:</span> <span class="string">root</span></span><br><span class="line">          <span class="attr">password:</span> <span class="string">root</span></span><br><span class="line">        <span class="comment"># 用户 users 数据源配置</span></span><br><span class="line">        <span class="attr">users:</span></span><br><span class="line">          <span class="attr">url:</span> <span class="string">jdbc:mysql://127.0.0.1:3306/labs-springboot2?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai</span></span><br><span class="line">          <span class="attr">driver-class-name:</span> <span class="string">com.mysql.cj.jdbc.Driver</span></span><br><span class="line">          <span class="attr">username:</span> <span class="string">root</span></span><br><span class="line">          <span class="attr">password:</span> <span class="string">root</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># mybatis 配置内容</span></span><br><span class="line"><span class="attr">mybatis:</span></span><br><span class="line">  <span class="attr">config-location:</span> <span class="string">classpath:mybatis-config.xml</span> <span class="comment"># 配置 MyBatis 配置文件路径</span></span><br><span class="line">  <span class="attr">mapper-locations:</span> <span class="string">classpath:mapper/*.xml</span> <span class="comment"># 配置 Mapper XML 地址</span></span><br><span class="line">  <span class="attr">type-aliases-package:</span> <span class="string">cn.javadog.labs.springboot2.datasource.dynamic.baomidou.one.dataobject</span> <span class="comment"># 配置数据库实体包路径</span></span><br></pre></td></tr></table></figure><ul><li><code>spring.datasource.dynamic</code> 配置项，设置 <code>dynamic-datasource-spring-boot-starter</code>动态数据源的配置内容。<ul><li><code>primary</code> 配置项，设置默认的数据源或者数据源组，默认值即为 master 。</li><li><code>datasource</code> 配置项，配置<strong>每个</strong>动态数据源。这里，我们配置了 <code>orders</code>、<code>users</code> 两个动态数据源。</li></ul></li><li><code>mybatis</code> 配置项，设置 <code>mybatis-spring-boot-starter</code> MyBatis 的配置内容。</li></ul><h2 id="3-4-MyBatis-配置文件"><a href="#3-4-MyBatis-配置文件" class="headerlink" title="3.4 MyBatis 配置文件"></a>3.4 MyBatis 配置文件</h2><p>在 <code>resources</code>目录下，创建 <code>mybatis-config.xml</code> 配置文件。配置如下：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version="1.0" encoding="UTF-8" ?&gt;</span></span><br><span class="line"><span class="meta">&lt;!DOCTYPE <span class="meta-keyword">configuration</span> <span class="meta-keyword">PUBLIC</span> <span class="meta-string">"-//mybatis.org//DTD Config 3.0//EN"</span> <span class="meta-string">"http://mybatis.org/dtd/mybatis-3-config.dtd"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">configuration</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">settings</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 使用驼峰命名法转换字段。 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">setting</span> <span class="attr">name</span>=<span class="string">"mapUnderscoreToCamelCase"</span> <span class="attr">value</span>=<span class="string">"true"</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">settings</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">typeAliases</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">typeAlias</span> <span class="attr">alias</span>=<span class="string">"Integer"</span> <span class="attr">type</span>=<span class="string">"java.lang.Integer"</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">typeAlias</span> <span class="attr">alias</span>=<span class="string">"Long"</span> <span class="attr">type</span>=<span class="string">"java.lang.Long"</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">typeAlias</span> <span class="attr">alias</span>=<span class="string">"HashMap"</span> <span class="attr">type</span>=<span class="string">"java.util.HashMap"</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">typeAlias</span> <span class="attr">alias</span>=<span class="string">"LinkedHashMap"</span> <span class="attr">type</span>=<span class="string">"java.util.LinkedHashMap"</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">typeAlias</span> <span class="attr">alias</span>=<span class="string">"ArrayList"</span> <span class="attr">type</span>=<span class="string">"java.util.ArrayList"</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">typeAlias</span> <span class="attr">alias</span>=<span class="string">"LinkedList"</span> <span class="attr">type</span>=<span class="string">"java.util.LinkedList"</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">typeAliases</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br></pre></td></tr></table></figure><p>因为在数据库中的表的字段，我们是使用下划线风格，而数据库实体的字段使用驼峰风格，所以通过 <code>mapUnderscoreToCamelCase = true</code> 来自动转换。</p><h2 id="3-5-实体类"><a href="#3-5-实体类" class="headerlink" title="3.5 实体类"></a>3.5 实体类</h2><p>在 <code>cn.javadog.labs.springboot2.datasource.dynamic.baomidou.one.dataobject</code> 包路径下，创建 <code>UserDO.java</code> 和 <code>OrderDO.java</code>类。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// OrderDO.java</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@Accessors</span>(chain = <span class="keyword">true</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">OrderDO</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 订单编号</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> Integer id;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 用户编号</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> Integer userId;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">toString</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">"OrderDO&#123;"</span> + <span class="string">"id="</span> + id + <span class="string">", userId="</span> + userId + <span class="string">'&#125;'</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// UserDO.java</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@Accessors</span>(chain = <span class="keyword">true</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UserDO</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 用户编号</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> Integer id;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 账号</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> String username;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">toString</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">"UserDO&#123;"</span> + <span class="string">"id="</span> + id + <span class="string">", username='"</span> + username + <span class="string">'\''</span> + <span class="string">'&#125;'</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>对应的创建表的 SQL 如下：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> <span class="string">`dynamic_datasource_users`</span> (</span><br><span class="line">  <span class="string">`id`</span> <span class="built_in">int</span>(<span class="number">11</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span> AUTO_INCREMENT <span class="keyword">COMMENT</span> <span class="string">'用户编号'</span>,</span><br><span class="line">  <span class="string">`username`</span> <span class="built_in">varchar</span>(<span class="number">64</span>) <span class="keyword">COLLATE</span> utf8mb4_bin <span class="keyword">DEFAULT</span> <span class="literal">NULL</span> <span class="keyword">COMMENT</span> <span class="string">'账号'</span>,</span><br><span class="line">  PRIMARY <span class="keyword">KEY</span> (<span class="string">`id`</span>),</span><br><span class="line">  <span class="keyword">UNIQUE</span> <span class="keyword">KEY</span> <span class="string">`idx_username`</span> (<span class="string">`username`</span>)</span><br><span class="line">) <span class="keyword">ENGINE</span>=<span class="keyword">InnoDB</span> <span class="keyword">DEFAULT</span> <span class="keyword">CHARSET</span>=utf8mb4 <span class="keyword">COLLATE</span>=utf8mb4_bin;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> <span class="string">`dynamic_datasource_orders`</span> (</span><br><span class="line">  <span class="string">`id`</span> <span class="built_in">int</span>(<span class="number">11</span>) <span class="keyword">DEFAULT</span> <span class="literal">NULL</span> <span class="keyword">COMMENT</span> <span class="string">'订单编号'</span>,</span><br><span class="line">  <span class="string">`user_id`</span> <span class="built_in">int</span>(<span class="number">16</span>) <span class="keyword">DEFAULT</span> <span class="literal">NULL</span> <span class="keyword">COMMENT</span> <span class="string">'用户编号'</span></span><br><span class="line">) <span class="keyword">ENGINE</span>=<span class="keyword">InnoDB</span> <span class="keyword">DEFAULT</span> <span class="keyword">CHARSET</span>=utf8mb4 <span class="keyword">COLLATE</span>=utf8mb4_bin <span class="keyword">COMMENT</span>=<span class="string">'订单表'</span>;</span><br></pre></td></tr></table></figure><h2 id="3-6-Mapper"><a href="#3-6-Mapper" class="headerlink" title="3.6 Mapper"></a>3.6 Mapper</h2><p>在 <code>cn.javadog.labs.springboot2.datasource.dynamic.baomidou.one.mapper</code>包路径下，创建 <code>UserDO.java</code> 和 <code>UserMapper.java</code> 接口。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// OrderMapper.java</span></span><br><span class="line"><span class="meta">@Repository</span></span><br><span class="line"><span class="meta">@DS</span>(DBConstants.DATASOURCE_ORDERS)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">OrderMapper</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function">OrderDO <span class="title">selectById</span><span class="params">(@Param(<span class="string">"id"</span>)</span> Integer id)</span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// UserMapper.java</span></span><br><span class="line"><span class="meta">@Repository</span></span><br><span class="line"><span class="meta">@DS</span>(DBConstants.DATASOURCE_USERS)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">UserMapper</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function">UserDO <span class="title">selectById</span><span class="params">(@Param(<span class="string">"id"</span>)</span> Integer id)</span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p><a href="https://github.com/yuyong725/labs-springboot2/blob/master/labs-springboot2-data-access/labs-springboot2-datasource-dynamic/labs-springboot2-datasource-dynamic-baomidou-01/src/main/java/cn/javadog/labs/springboot2/datasource/dynamic/baomidou/one/constant/DBConstants.java" target="_blank" rel="noopener">DBConstants.java</a> 类，枚举了 <code>DATASOURCE_ORDERS</code> 和 <code>DATASOURCE_USERS</code> 两个数据源。</p></li><li><p><code>@DS</code> 注解，是 <code>dynamic-datasource-spring-boot-starter</code> 提供，可添加在 Service 或 Mapper 的类/接口上，或者方法上。在其 <code>value</code> 属性种，填写数据源的名字。</p></li><li><p>OrderMapper 接口上，我们添加了 <code>@DS(DBConstants.DATASOURCE_ORDERS)</code> 注解，访问 <code>orders</code> 数据源。</p><ul><li>UserMapper 接口上，我们添加了 <code>@DS(DBConstants.DATASOURCE_USERS)</code> 注解，访问 <code>users</code> 数据源。</li></ul></li><li><p>为了让整个测试用例精简，我们在 OrderMapper 和 UserMapper 中，只添加了根据编号查询单条记录的方法。</p></li></ul><p>在 <code>resources/mapper</code> 路径下，创建 <code>OrderMapper.xml</code> 配置文件。代码如下：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- OrderMapper.xml --&gt;</span></span><br><span class="line"><span class="meta">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span></span><br><span class="line"><span class="meta">&lt;!DOCTYPE <span class="meta-keyword">mapper</span> <span class="meta-keyword">PUBLIC</span> <span class="meta-string">"-//mybatis.org//DTD Mapper 3.0//EN"</span> <span class="meta-string">"http://mybatis.org/dtd/mybatis-3-mapper.dtd"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">"cn.javadog.labs.springboot2.datasource.dynamic.baomidou.one.mapper.OrderMapper"</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">sql</span> <span class="attr">id</span>=<span class="string">"FIELDS"</span>&gt;</span></span><br><span class="line">        id, user_id</span><br><span class="line">    <span class="tag">&lt;/<span class="name">sql</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">"selectById"</span> <span class="attr">parameterType</span>=<span class="string">"Integer"</span> <span class="attr">resultType</span>=<span class="string">"OrderDO"</span>&gt;</span></span><br><span class="line">        SELECT</span><br><span class="line">            <span class="tag">&lt;<span class="name">include</span> <span class="attr">refid</span>=<span class="string">"FIELDS"</span> /&gt;</span></span><br><span class="line">        FROM dynamic_datasource_orders</span><br><span class="line">        WHERE id = #&#123;id&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- UserMapper.xml --&gt;</span></span><br><span class="line"><span class="meta">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span></span><br><span class="line"><span class="meta">&lt;!DOCTYPE <span class="meta-keyword">mapper</span> <span class="meta-keyword">PUBLIC</span> <span class="meta-string">"-//mybatis.org//DTD Mapper 3.0//EN"</span> <span class="meta-string">"http://mybatis.org/dtd/mybatis-3-mapper.dtd"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">"cn.javadog.labs.springboot2.datasource.dynamic.baomidou.one.mapper.UserMapper"</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">sql</span> <span class="attr">id</span>=<span class="string">"FIELDS"</span>&gt;</span></span><br><span class="line">        id, username</span><br><span class="line">    <span class="tag">&lt;/<span class="name">sql</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">"selectById"</span> <span class="attr">parameterType</span>=<span class="string">"Integer"</span> <span class="attr">resultType</span>=<span class="string">"UserDO"</span>&gt;</span></span><br><span class="line">        SELECT</span><br><span class="line">            <span class="tag">&lt;<span class="name">include</span> <span class="attr">refid</span>=<span class="string">"FIELDS"</span> /&gt;</span></span><br><span class="line">        FROM dynamic_datasource_users</span><br><span class="line">        WHERE id = #&#123;id&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure><h2 id="3-7-简单测试"><a href="#3-7-简单测试" class="headerlink" title="3.7 简单测试"></a>3.7 简单测试</h2><p>创建 <code>UserMapperTest</code> 和 <code>OrderMapperTest</code> 测试类，我们来测试一下简单的 UserMapper 和 OrderMapper 的每个操作。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// OrderMapperTest.java</span></span><br><span class="line"><span class="meta">@RunWith</span>(SpringRunner<span class="class">.<span class="keyword">class</span>)</span></span><br><span class="line"><span class="class">@<span class="title">SpringBootTest</span></span></span><br><span class="line"><span class="class"><span class="title">public</span> <span class="title">class</span> <span class="title">OrderMapperTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> OrderMapper orderMapper;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testSelectById</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        OrderDO order = orderMapper.selectById(<span class="number">1</span>);</span><br><span class="line">        System.out.println(order);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// UserMapperTest.java</span></span><br><span class="line"><span class="meta">@RunWith</span>(SpringRunner<span class="class">.<span class="keyword">class</span>)</span></span><br><span class="line"><span class="class">@<span class="title">SpringBootTest</span></span></span><br><span class="line"><span class="class"><span class="title">public</span> <span class="title">class</span> <span class="title">UserMapperTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserMapper userMapper;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testSelectById</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        UserDO user = userMapper.selectById(<span class="number">1</span>);</span><br><span class="line">        System.out.println(user);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>胖友自己跑下测试用例。如果跑通，说明配置就算成功了。</p><h2 id="3-8-详细测试"><a href="#3-8-详细测试" class="headerlink" title="3.8 详细测试"></a>3.8 详细测试</h2><p>在本小节，我们会编写 5 个测试用例，尝试阐述 <code>dynamic-datasource-spring-boot-starter</code> 在和 Spring 事务结合碰到的情况，以便胖友更好的使用。当然，这个不仅仅是 <code>dynamic-datasource-spring-boot-starter</code> 独有的，而是方案一【基于 Spring AbstractRoutingDataSource 做拓展】都存在的情况。</p><p>在 <code>cn.javadog.labs.springboot2.datasource.dynamic.baomidou.one.service</code> 包路径下，创建 <code>OrderService.java</code> 类。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// OrderService.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">OrderService</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> OrderMapper orderMapper;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserMapper userMapper;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> OrderService <span class="title">self</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> (OrderService) AopContext.currentProxy();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">method01</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// ... 省略代码</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Transactional</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">method02</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// ... 省略代码</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">method03</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// ... 省略代码</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">method04</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// ... 省略代码</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Transactional</span></span><br><span class="line">    <span class="meta">@DS</span>(DBConstants.DATASOURCE_ORDERS)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">method05</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// ... 省略代码</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><code>#self()</code> 方法，通过 AopContext 获得自己这个代理对象。举个例子，在 <code>#method01()</code> 方法中，如果直接使用 <code>this.method02()</code> 方法进行调用，因为 <code>this</code> 代表的是 OrderService Bean 自身，而不是其 AOP 代理对象。这样会导致，无法触发 AOP 的逻辑，在此处，就是 Spring 事务的逻辑。因此，我们通过 AopContext 获得自己这个代理对象。</li><li>每一个 <code>#methodXX()</code> 方法，都代表一个测试用例，胖友可以使用 <a href="https://github.com/yuyong725/labs-springboot2/blob/master/labs-springboot2-data-access/labs-springboot2-datasource-dynamic/labs-springboot2-datasource-dynamic-baomidou-01/src/test/java/cn/javadog/labs/springboot2/datasource/dynamic/baomidou/one/service/OrderServiceTest.java" target="_blank" rel="noopener">OrderServiceTest</a> 进行测试。</li></ul><p>下面，我们来一个一个看。</p><p><strong>场景一：<code>#method01()</code></strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// OrderService.java</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">method01</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 查询订单</span></span><br><span class="line">    OrderDO order = orderMapper.selectById(<span class="number">1</span>);</span><br><span class="line">    System.out.println(order);</span><br><span class="line">    <span class="comment">// 查询用户</span></span><br><span class="line">    UserDO user = userMapper.selectById(<span class="number">1</span>);</span><br><span class="line">    System.out.println(user);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>方法未使用 <code>@Transactional</code> 注解，不会开启事务。</li><li>对于 OrderMapper 和 UserMapper 的查询操作，分别使用其接口上的 <code>@DS</code> 注解，找到对应的数据源，执行操作。</li><li>这样一看，在未开启事务的情况下，我们已经能够自由的使用多数据源落。</li></ul><p><strong>场景二：<code>#method02()</code></strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// OrderService.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Transactional</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">method02</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 查询订单</span></span><br><span class="line">    OrderDO order = orderMapper.selectById(<span class="number">1</span>);</span><br><span class="line">    System.out.println(order);</span><br><span class="line">    <span class="comment">// 查询用户</span></span><br><span class="line">    UserDO user = userMapper.selectById(<span class="number">1</span>);</span><br><span class="line">    System.out.println(user);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>和 <code>#method01()</code> 方法，差异在于，方法上增加了 <code>@Transactional</code> 注解，声明要使用 Spring 事务。</p></li><li><p>执行方法，抛出如下异常：</p><figure class="highlight verilog"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Caused by: com<span class="variable">.mysql</span><span class="variable">.jdbc</span><span class="variable">.exceptions</span><span class="variable">.jdbc4</span><span class="variable">.MySQLSyntaxErrorException</span>: Table 'labs-springboot2<span class="variable">.dynamic_datasource_orders</span>' doesn't exist</span><br></pre></td></tr></table></figure><ul><li>在执行 OrderMapper 查询订单操作时，抛出在 <code>labs-springboot2</code> 库中，不存在 <code>dynamic_datasource_orders</code> 表。</li></ul></li><li><p>这是为什么呢？咱不是在 OrderMapper 上，声明使用 <code>orders</code> 数据源了么？结果为什么会使用 <code>users</code> 数据库，路由到 <code>labs-springboot2</code> 库上呢。</p><ul><li>这里，就和 Spring 事务的实现机制有关系。因为方法添加了 <code>@Transactional</code> 注解，Spring 事务就会生效。此时，Spring <a href="https://github.com/spring-projects/spring-framework/blob/master/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionInterceptor.java" target="_blank" rel="noopener">TransactionInterceptor</a> 会通过 AOP 拦截该方法，创建事务。而创建事务，势必就会获得数据源。那么，TransactionInterceptor 会使用 Spring <a href="https://github.com/spring-projects/spring-framework/blob/master/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceTransactionManager.java" target="_blank" rel="noopener">DataSourceTransactionManager</a> 创建事务，并将事务信息通过 ThreadLocal 绑定在当前线程。</li><li>而事务信息，就包括事务对应的 Connection 连接。那也就意味着，还没走到 OrderMapper 的查询操作，Connection 就已经被创建出来了。并且，因为事务信息会和当前线程绑定在一起，在 OrderMapper 在查询操作需要获得 Connection 时，就直接拿到当前线程绑定的 Connection ，而不是 OrderMapper 添加 <code>@DS</code> 注解所对应的 DataSource 所对应的 Connection 。</li><li>OK ，那么我们现在可以把问题聚焦到 DataSourceTransactionManager 是怎么获取 DataSource 从而获得 Connection 的了。对于每个 DataSourceTransactionManager 数据库事务管理器，创建时都会传入其需要管理的 DataSource 数据源。在使用 <code>dynamic-datasource-spring-boot-starter</code> 时，它创建了一个 DynamicRoutingDataSource ，传入到 DataSourceTransactionManager 中。</li><li>而 DynamicRoutingDataSource 负责管理我们配置的多个数据源。例如说，本示例中就管理了 <code>orders</code>、<code>users</code> 两个数据源，并且默认使用 <code>users</code> 数据源。那么在当前场景下，DynamicRoutingDataSource 需要基于 <code>@DS</code> 获得数据源名，从而获得对应的 DataSource ，结果因为我们在 Service 方法上，并没有添加 <code>@DS</code> 注解，所以它只好返回默认数据源，也就是 <code>users</code> 。故此，就发生了 <code>Table &#39;labs-springboot2.dynamic_datasource_orders&#39; doesn&#39;t exist</code> 的异常。</li><li>咳咳咳，这里涉及 Spring 事务的实现机制，如果胖友不是很了解源码会比较懵逼，推荐可以尝试将 <a href="https://github.com/spring-projects/spring-framework/blob/master/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionInterceptor.java" target="_blank" rel="noopener">TransactionInterceptor</a> 作为入口，进行调试。当然，也欢迎胖友留言。</li></ul></li></ul><p><strong>场景三：<code>#method03()</code></strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// OrderService.java</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">method03</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 查询订单</span></span><br><span class="line">    self().method031();</span><br><span class="line">    <span class="comment">// 查询用户</span></span><br><span class="line">    self().method032();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* 报错，因为此时获取的是 primary 对应的 DataSource ，即 users 。</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="meta">@Transactional</span>(rollbackFor = Exception<span class="class">.<span class="keyword">class</span>)</span></span><br><span class="line"><span class="class"><span class="title">public</span> <span class="title">void</span> <span class="title">method031</span>() </span>&#123;</span><br><span class="line">		OrderDO order = orderMapper.selectById(<span class="number">1</span>);</span><br><span class="line">    System.out.println(order);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Transactional</span>(rollbackFor = Exception<span class="class">.<span class="keyword">class</span>)</span></span><br><span class="line"><span class="class"><span class="title">public</span> <span class="title">void</span> <span class="title">method032</span>() </span>&#123;</span><br><span class="line">    UserDO user = userMapper.selectById(<span class="number">1</span>);</span><br><span class="line">    System.out.println(user);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>执行方法，抛出如下异常：<code>Table &#39;labs-springboot2.dynamic_datasource_orders&#39; doesn&#39;t exist</code><ul><li>其实，场景三和场景二是等价的。</li><li>其实，这样调整后，因为 <code>this</code> 不是代理对象，所以 <code>#method031()</code> 和 <code>#method032()</code> 方法上的 <code>@Transactional</code> 直接没有作用，Spring 事务根本没有生效。但是依然只会连接一个库</li></ul></li></ul><p><strong>场景四：<code>#method04()</code></strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// OrderService.java</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">method04</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 查询订单</span></span><br><span class="line">    self().method041();</span><br><span class="line">    <span class="comment">// 查询用户</span></span><br><span class="line">    self().method042();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Transactional</span></span><br><span class="line"><span class="meta">@DS</span>(DBConstants.DATASOURCE_ORDERS)</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">method041</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    OrderDO order = orderMapper.selectById(<span class="number">1</span>);</span><br><span class="line">    System.out.println(order);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Transactional</span></span><br><span class="line"><span class="meta">@DS</span>(DBConstants.DATASOURCE_USERS)</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">method042</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    UserDO user = userMapper.selectById(<span class="number">1</span>);</span><br><span class="line">    System.out.println(user);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>和 <code>@method03()</code> 方法，差异在于，<code>#method041()</code> 和 <code>#method042()</code> 方法上，添加 <code>@DS</code> 注解，声明对应使用的 DataSource 。</li><li>执行方法，正常结束，未抛出异常。是不是觉得有点奇怪？</li><li>在执行 <code>#method041()</code> 方法前，因为有 <code>@Transactional</code> 注解，所以 Spring 事务机制触发。DynamicRoutingDataSource 根据 <code>@DS</code> 注解，获得对应的 <code>orders</code> 的 DataSource ，从而获得 Connection 。所以后续 OrderMapper 执行查询操作时，即使使用的是线程绑定的 Connection ，也可能不会报错。嘿嘿，实际上，此时 OrderMapper 上的 <code>@DS</code> 注解，也没有作用。</li><li>对于 <code>#method042()</code> ，也是同理。但是，我们上面不是提了 Connection 会绑定在当前线程么？那么，在 <code>#method042()</code> 方法中，应该使用的是 <code>#method041()</code> 的 <code>orders</code> 对应的 Connection 呀。在 Spring 事务机制中，在一个事务执行完成后，会将事务信息和当前线程解绑。所以，在执行 <code>#method042()</code> 方法前，又可以执行一轮事务的逻辑。</li><li>【重要】总的来说，对于声明了 <code>@Transactional</code> 的 Service 方法上，也同时通过 <code>@DS</code> 声明对应的数据源。</li></ul><p><strong>场景五：<code>#method05()</code></strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// OrderService.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Transactional</span></span><br><span class="line"><span class="meta">@DS</span>(DBConstants.DATASOURCE_ORDERS)</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">method05</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 查询订单</span></span><br><span class="line">    OrderDO order = orderMapper.selectById(<span class="number">1</span>);</span><br><span class="line">    System.out.println(order);</span><br><span class="line">    <span class="comment">// 查询用户</span></span><br><span class="line">    self().method052();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Transactional</span>(propagation = Propagation.REQUIRES_NEW)</span><br><span class="line"><span class="meta">@DS</span>(DBConstants.DATASOURCE_USERS)</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">method052</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    UserDO user = userMapper.selectById(<span class="number">1</span>);</span><br><span class="line">    System.out.println(user);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>和 <code>@method04()</code> 方法，差异在于，我们直接在 <code>#method05()</code> 方法中，<strong>此时处于一个事务中</strong>，直接调用了 <code>#method052()</code> 方法。</p></li><li><p>执行方法，正常结束，未抛出异常。是不是觉得有点奇怪？</p></li><li><p>我们仔细看看 <code>#method052()</code>方法，我们添加的 <code>@Transactionl</code>注解，使用的事务传播级别是</p><p><code>Propagation.REQUIRES_NEW</code>。此时，在执行 <code>#method052()</code>方法之前，TransactionInterceptor 会将原事务挂起，暂时性的将原事务信息和当前线程解绑。</p><ul><li>所以，在执行 <code>#method052()</code> 方法前，又可以执行一轮事务的逻辑。</li><li>之后，在执行 <code>#method052()</code> 方法完成后，会将原事务<strong>恢复</strong>，重新将原事务信息和当前线程绑定。</li></ul></li><li><p>编写这个场景的目的，是想告诉胖友，如果在使用方案一【基于 Spring AbstractRoutingDataSource 做拓展】，在事务中时，如何切换数据源。当然，一旦切换数据源，可能产生多个事务，就会碰到多个事务一致性的问题，也就是分布式事务。</p></li></ul><p>五个场景，胖友在好好理解。可以尝试调试下源码，更好的帮助理解。</p><p>咳咳咳，如果有解释不到位的地方，欢迎胖友留言。</p><h1 id="4-baomidou-读写分离"><a href="#4-baomidou-读写分离" class="headerlink" title="4. baomidou 读写分离"></a>4. baomidou 读写分离</h1><blockquote><p>示例代码对应仓库：<a href="https://github.com/yuyong725/labs-springboot2/tree/master/labs-springboot2-data-access/labs-springboot2-datasource-dynamic/labs-springboot2-datasource-dynamic-baomidou-02" target="_blank" rel="noopener">labs-springboot2-datasource-dynamic-baomidou-02</a> 。</p></blockquote><p>在绝大多数情况下，我们使用多数据源的目的，是为了实现读写分离。所以，在本小节中，我们来使用 <code>dynamic-datasource-spring-boot-starter</code>，实现一个读写分离的示例。</p><h2 id="4-1-引入依赖"><a href="#4-1-引入依赖" class="headerlink" title="4.1 引入依赖"></a>4.1 引入依赖</h2><p>和 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「3.1 引入依赖」</a> 一致。</p><h2 id="4-2-BaomidouApplication"><a href="#4-2-BaomidouApplication" class="headerlink" title="4.2 BaomidouApplication"></a>4.2 BaomidouApplication</h2><p>和 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「3.2 BaomidouApplication」</a> 一致。</p><h2 id="4-3-应用配置文件"><a href="#4-3-应用配置文件" class="headerlink" title="4.3 应用配置文件"></a>4.3 应用配置文件</h2><p>在 <code>resources</code> 目录下，创建 <code>application.yaml</code> 配置文件。配置如下：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">datasource:</span></span><br><span class="line">    <span class="comment"># dynamic-datasource-spring-boot-starter 动态数据源的配置内容</span></span><br><span class="line">    <span class="attr">dynamic:</span></span><br><span class="line">      <span class="attr">primary:</span> <span class="string">master</span> <span class="comment"># 设置默认的数据源或者数据源组，默认值即为 master</span></span><br><span class="line">      <span class="attr">datasource:</span></span><br><span class="line">        <span class="comment"># 订单 orders 数据源配置</span></span><br><span class="line">        <span class="attr">master:</span></span><br><span class="line">          <span class="attr">url:</span> <span class="string">jdbc:mysql://127.0.0.1:3306/labs-springboot2-multiple?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai</span></span><br><span class="line">          <span class="attr">driver-class-name:</span> <span class="string">com.mysql.cj.jdbc.Driver</span></span><br><span class="line">          <span class="attr">username:</span> <span class="string">root</span></span><br><span class="line">          <span class="attr">password:</span> <span class="string">root</span></span><br><span class="line">        <span class="comment"># 订单 orders 从库1数据源配置</span></span><br><span class="line">        <span class="attr">slave_1:</span></span><br><span class="line">          <span class="attr">url:</span> <span class="string">jdbc:mysql://127.0.0.1:3306/labs-springboot2?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai</span></span><br><span class="line">          <span class="attr">driver-class-name:</span> <span class="string">com.mysql.cj.jdbc.Driver</span></span><br><span class="line">          <span class="attr">username:</span> <span class="string">root</span></span><br><span class="line">          <span class="attr">password:</span> <span class="string">root</span></span><br><span class="line">        <span class="comment"># 订单 orders 从库2数据源配置</span></span><br><span class="line"><span class="comment">#        slave_2:</span></span><br><span class="line"><span class="comment">#          url: jdbc:mysql://127.0.0.1:3306/labs-springboot2-multiple?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai</span></span><br><span class="line"><span class="comment">#          driver-class-name: com.mysql.cj.jdbc.Driver</span></span><br><span class="line"><span class="comment">#          username: root</span></span><br><span class="line"><span class="comment">#          password: root</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># mybatis 配置内容</span></span><br><span class="line"><span class="attr">mybatis:</span></span><br><span class="line">  <span class="attr">config-location:</span> <span class="string">classpath:mybatis-config.xml</span> <span class="comment"># 配置 MyBatis 配置文件路径</span></span><br><span class="line">  <span class="attr">mapper-locations:</span> <span class="string">classpath:mapper/*.xml</span> <span class="comment"># 配置 Mapper XML 地址</span></span><br><span class="line">  <span class="attr">type-aliases-package:</span> <span class="string">cn.javadog.labs.springboot2.datasource.dynamic.baomidou.two.dataobject</span> <span class="comment"># 配置数据库实体包路径</span></span><br></pre></td></tr></table></figure><ul><li><p>相比「3.3 应用配置」来说，我们配置了订单库的多个数据源：</p><ul><li><code>master</code> ：订单库的主库。</li></ul></li><li><p><code>slave_1</code> ：订单库的从库。</p></li><li><p>在 <code>dynamic-datasource-spring-boot-starter</code>中，多个相同角色的数据源可以形成一个数据源组。判断标准是，数据源名以下划线 <code>_</code> 分隔后的首部即为组名。例如说，<code>slave_1</code> 和 <code>slave_2</code> 形成了 <code>slave</code> 组。</p><ul><li>我们可以使用 <code>@DS(&quot;slave_1&quot;)</code> 或 <code>@DS(&quot;slave_2&quot;)</code> 注解，明确访问数据源组的指定数据源。</li></ul></li><li><p>也可以使用 <code>@DS(&quot;slave&quot;)</code> 注解，此时会负载均衡，选择分组中的某个数据源进行访问。目前，负载均衡默认采用轮询的方式。</p></li><li><p>因为卡姆本地并未搭建 MySQL 一主多从的环境，所以是通过创建了 <code>labs-springboot2</code>库，手动模拟作为 <code>labs-springboot2-multiple</code> 的从库。</p></li></ul><h2 id="4-4-MyBatis-配置文件"><a href="#4-4-MyBatis-配置文件" class="headerlink" title="4.4 MyBatis 配置文件"></a>4.4 MyBatis 配置文件</h2><p>和 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「3.4 MyBatis 配置文件」</a> 一致。</p><h2 id="4-5-OrderDO"><a href="#4-5-OrderDO" class="headerlink" title="4.5 OrderDO"></a>4.5 OrderDO</h2><p>只使用 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「3.5 实体类」</a> 的 <code>OrderDO.java</code> 类。</p><h2 id="4-6-OrderMapper"><a href="#4-6-OrderMapper" class="headerlink" title="4.6 OrderMapper"></a>4.6 OrderMapper</h2><p>在 <code>cn.javadog.labs.springboot2.datasource.dynamic.baomidou.two.mapper</code> 包路径下，创建 <code>OrderMapper.java</code>接口。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// OrderMapper.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Repository</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">OrderMapper</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">     <span class="meta">@DS</span>(DBConstants.DATASOURCE_SLAVE)</span><br><span class="line">    <span class="function">OrderDO <span class="title">selectById</span><span class="params">(@Param(<span class="string">"id"</span>)</span> Integer id)</span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@DS</span>(DBConstants.DATASOURCE_MASTER)</span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">insert</span><span class="params">(OrderDO entity)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@DS</span>(DBConstants.DATASOURCE_SLAVE)</span><br><span class="line">    <span class="function">OrderDO <span class="title">selectByUserId</span><span class="params">(@Param(<span class="string">"userId"</span>)</span> Integer userId)</span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><code>DBConstants.java</code>类，枚举了 <code>DATASOURCE_MASTER</code> 和 <code>DATASOURCE_SLAVE</code> 两个数据源。</li><li>对 <code>#selectById(Integer id)</code> 读操作，我们配置了 <code>@DS(DBConstants.DATASOURCE_SLAVE)</code> ，访问从库。</li><li>对 <code>#insert(OrderDO entity)</code> 写操作，我们配置了 <code>@DS(DBConstants.DATASOURCE_MASTER)</code> ，访问主库。</li></ul><p>在 <code>resources/mapper</code> 路径下，创建 <code>OrderMapper.xml</code> 配置文件。代码如下：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span></span><br><span class="line"><span class="meta">&lt;!DOCTYPE <span class="meta-keyword">mapper</span> <span class="meta-keyword">PUBLIC</span> <span class="meta-string">"-//mybatis.org//DTD Mapper 3.0//EN"</span> <span class="meta-string">"http://mybatis.org/dtd/mybatis-3-mapper.dtd"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">"cn.javadog.labs.springboot2.datasource.dynamic.baomidou.two.mapper.OrderMapper"</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">sql</span> <span class="attr">id</span>=<span class="string">"FIELDS"</span>&gt;</span></span><br><span class="line">        id, user_id</span><br><span class="line">    <span class="tag">&lt;/<span class="name">sql</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">"selectById"</span> <span class="attr">parameterType</span>=<span class="string">"Integer"</span> <span class="attr">resultType</span>=<span class="string">"OrderDO"</span>&gt;</span></span><br><span class="line">        SELECT</span><br><span class="line">        <span class="tag">&lt;<span class="name">include</span> <span class="attr">refid</span>=<span class="string">"FIELDS"</span> /&gt;</span></span><br><span class="line">        FROM dynamic_datasource_orders</span><br><span class="line">        WHERE id = #&#123;id&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">insert</span> <span class="attr">id</span>=<span class="string">"insert"</span> <span class="attr">parameterType</span>=<span class="string">"OrderDO"</span> <span class="attr">useGeneratedKeys</span>=<span class="string">"true"</span> <span class="attr">keyProperty</span>=<span class="string">"id"</span>&gt;</span></span><br><span class="line">        INSERT INTO dynamic_datasource_orders (</span><br><span class="line">            user_id</span><br><span class="line">        ) VALUES (</span><br><span class="line">            #&#123;userId&#125;</span><br><span class="line">        )</span><br><span class="line">    <span class="tag">&lt;/<span class="name">insert</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">"selectByUserId"</span> <span class="attr">parameterType</span>=<span class="string">"Integer"</span> <span class="attr">resultType</span>=<span class="string">"OrderDO"</span>&gt;</span></span><br><span class="line">        SELECT</span><br><span class="line">        <span class="tag">&lt;<span class="name">include</span> <span class="attr">refid</span>=<span class="string">"FIELDS"</span> /&gt;</span></span><br><span class="line">        FROM dynamic_datasource_orders</span><br><span class="line">        WHERE user_id = #&#123;userId&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure><h2 id="3-7-简单测试-1"><a href="#3-7-简单测试-1" class="headerlink" title="3.7 简单测试"></a>3.7 简单测试</h2><p>创建 <code>UserMapperTest</code>测试类，我们来测试一下简单的 UserMapper 的读写操作。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// UserMapperTest.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@RunWith</span>(SpringRunner<span class="class">.<span class="keyword">class</span>)</span></span><br><span class="line"><span class="class">@<span class="title">SpringBootTest</span></span></span><br><span class="line"><span class="class"><span class="title">public</span> <span class="title">class</span> <span class="title">OrderMapperTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> OrderMapper orderMapper;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testSelectById</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">            OrderDO order = orderMapper.selectById(<span class="number">1</span>);</span><br><span class="line">            System.out.println(order);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testInsert</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        OrderDO order = <span class="keyword">new</span> OrderDO();</span><br><span class="line">        order.setUserId(<span class="number">10</span>);</span><br><span class="line">        orderMapper.insert(order);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>胖友自己跑下测试用例。如果跑通，说明配置就算成功了。</p><p>另外，在 <code>#testSelectById()</code> 测试方法中，为了看看 <code>slave</code> 分组是不是真的在负载均衡。所以在数据库中，分别插入数据如下。（卡姆只设置了一个从库，因此这部分没有测试）</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">主库：[id &#x3D; 1, user_id &#x3D; 1]</span><br><span class="line">从库 01：[id &#x3D; 1, user_id &#x3D; 2]</span><br><span class="line">从库 02：[id &#x3D; 1, user_id &#x3D; 3]</span><br></pre></td></tr></table></figure><ul><li>这样，通过手动设置相同 <code>id = 1</code> 的记录，对应不同的 <code>user_id</code> ，那么我们就可以观察 <code>#testSelectById()</code> 测试方法的输出结果。如果是，<code>user_id = 2</code> 和 <code>user_i = 3</code> 循环输出，说明就正常了。</li></ul><h2 id="3-8-详细测试-1"><a href="#3-8-详细测试-1" class="headerlink" title="3.8 详细测试"></a>3.8 详细测试</h2><p>在 <code>cn.javadog.labs.springboot2.datasource.dynamic.baomidou.two.service</code>包路径下，创建 <code>OrderService.java</code>类。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// OrderService.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">OrderService</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> OrderMapper orderMapper;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Transactional</span>(rollbackFor = Exception<span class="class">.<span class="keyword">class</span>)</span></span><br><span class="line"><span class="class">    @<span class="title">DS</span>(<span class="title">DBConstants</span>.<span class="title">DATASOURCE_MASTER</span>)</span></span><br><span class="line"><span class="class">    <span class="title">public</span> <span class="title">void</span> <span class="title">add</span>(<span class="title">OrderDO</span> <span class="title">order</span>) </span>&#123;</span><br><span class="line">        <span class="comment">// 这里先假模假样的读取一下，</span></span><br><span class="line">        OrderDO exists = orderMapper.selectByUserId(order.getUserId());</span><br><span class="line">        System.out.println(exists);</span><br><span class="line">        <span class="keyword">if</span> (Objects.isNull(exists)) &#123;</span><br><span class="line">            <span class="comment">// 插入订单</span></span><br><span class="line">            orderMapper.insert(order);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> OrderDO <span class="title">findById</span><span class="params">(Integer id)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> orderMapper.selectById(id);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>对于 <code>#add(OrderDO order)</code> 方法，我们希望在 <code>@Transactional</code> 声明的事务中，读操作也访问主库，所以声明了 <code>@DS(DBConstants.DATASOURCE_MASTER)</code> 。因此，后续的所有 OrderMapper 的操作，都访问的是订单库的 <code>MASTER</code> 数据源。</p><ul><li>对于 <code>#findById(Integer id)</code> 方法，读取指定订单信息，使用 OrderMapper 的 <code>#selectById(Integer id)</code> 配置的 <code>SLAVE</code> 数据源即可。</li></ul><p>创建 <code>OrderServiceTest</code> 测试类，测试 OrderService 的读写逻辑。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// OrderServiceTest.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@RunWith</span>(SpringRunner<span class="class">.<span class="keyword">class</span>)</span></span><br><span class="line"><span class="class">@<span class="title">SpringBootTest</span></span></span><br><span class="line"><span class="class"><span class="title">public</span> <span class="title">class</span> <span class="title">OrderServiceTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> OrderService orderService;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testAdd</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        OrderDO order = <span class="keyword">new</span> OrderDO();</span><br><span class="line">        order.setUserId(<span class="number">20</span>);</span><br><span class="line">        orderService.add(order);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testFindById</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        OrderDO order = orderService.findById(<span class="number">20</span>);</span><br><span class="line">        System.out.println(order);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>胖友自己跑下测试用例。如果跑通，说明配置就算成功了。</li></ul><p>另外，如果胖友的业务场景，是纯的读写分离，可以看看 <a href="https://gitee.com/baomidou/dynamic-datasource-spring-boot-starter/wikis/pages?sort_id=1339041&doc_id=147063" target="_blank" rel="noopener">《纯读写分离(mybatis 环境)》</a> 文档。</p><h1 id="5-MyBatis-多数据源"><a href="#5-MyBatis-多数据源" class="headerlink" title="5. MyBatis 多数据源"></a>5. MyBatis 多数据源</h1><blockquote><p>示例代码对应仓库：<a href="https://github.com/yuyong725/labs-springboot2/tree/master/labs-springboot2-data-access/labs-springboot2-datasource-dynamic/labs-springboot2-datasource-dynamic-mybatis" target="_blank" rel="noopener">labs-springboot2-datasource-dynamic-mybatis</a> 。</p></blockquote><p>本小节，我们会基于方案二【不同操作类，固定数据源】的方式，实现 MyBatis 多数据源。</p><p>整个配置过程会相对繁琐，胖友请保持耐心。</p><p>如果胖友对 Spring Data JPA 不了解的话，可以看看 <a href="https://calm_java.gitee.io/blog/2020/01/08/labs-springboot2-mybatis/">《Spring Boot MyBatis 入门》</a> 文章。</p><h2 id="5-1-引入依赖"><a href="#5-1-引入依赖" class="headerlink" title="5.1 引入依赖"></a>5.1 引入依赖</h2><p>在 <code>pom.xml</code> 文件中，引入相关依赖。</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">project</span> <span class="attr">xmlns</span>=<span class="string">"http://maven.apache.org/POM/4.0.0"</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xmlns:xsi</span>=<span class="string">"http://www.w3.org/2001/XMLSchema-instance"</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xsi:schemaLocation</span>=<span class="string">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>labs-springboot2-datasource-dynamic<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>cn.javadog<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.0-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">modelVersion</span>&gt;</span>4.0.0<span class="tag">&lt;/<span class="name">modelVersion</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>labs-springboot2-datasource-dynamic-mybatis<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 实现对数据库连接池的自动化配置 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-jdbc<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 本示例，我们使用 MySQL --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>mysql<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>mysql-connector-java<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- MyBatis 相关依赖 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.mybatis.spring.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>mybatis-spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 保证 Spring AOP 相关的依赖包 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-aspects<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 方便等会写单元测试 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-test<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">scope</span>&gt;</span>test<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br></pre></td></tr></table></figure><ul><li>具体每个依赖的作用，胖友自己认真看下添加的所有注释噢。</li><li>对于 <code>mybatis-spring-boot-starter</code> 依赖，这里并不使用它实现对 MyBatis 的自动化配置。这么引入，只是单纯方便，实际只要引入 <a href="https://mvnrepository.com/artifact/org.mybatis/mybatis" target="_blank" rel="noopener">mybatis</a> 和 <a href="https://mvnrepository.com/artifact/org.mybatis/mybatis-spring" target="_blank" rel="noopener">mybatis-spring</a> 即可。</li></ul><h2 id="5-2-Application"><a href="#5-2-Application" class="headerlink" title="5.2 Application"></a>5.2 Application</h2><p>创建 <code>Application.java</code> 类，代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@EnableAspectJAutoProxy</span>(exposeProxy = <span class="keyword">true</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Application</span> </span>&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>我们并没有添加 <code>@MapperScan</code> 注解，为什么呢?答案我们在 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「5.5 配置类」</a> 上看。</li></ul><h2 id="5-3-应用配置文件"><a href="#5-3-应用配置文件" class="headerlink" title="5.3 应用配置文件"></a>5.3 应用配置文件</h2><p>在 <code>resources</code> 目录下，创建 <code>application.yaml</code> 配置文件。配置如下：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">datasource:</span></span><br><span class="line">    <span class="comment"># 订单 orders 数据源配置</span></span><br><span class="line">    <span class="attr">orders:</span></span><br><span class="line">      <span class="attr">jdbc-url:</span> <span class="string">jdbc:mysql://127.0.0.1:3306/labs-springboot2-multiple?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai</span></span><br><span class="line">      <span class="attr">driver-class-name:</span> <span class="string">com.mysql.cj.jdbc.Driver</span></span><br><span class="line">      <span class="attr">username:</span> <span class="string">root</span></span><br><span class="line">      <span class="attr">password:</span> <span class="string">root</span></span><br><span class="line">    <span class="comment"># 用户 users 数据源配置</span></span><br><span class="line">    <span class="attr">users:</span></span><br><span class="line">      <span class="attr">jdbc-url:</span> <span class="string">jdbc:mysql://127.0.0.1:3306/labs-springboot2?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai</span></span><br><span class="line">      <span class="attr">driver-class-name:</span> <span class="string">com.mysql.cj.jdbc.Driver</span></span><br><span class="line">      <span class="attr">username:</span> <span class="string">root</span></span><br><span class="line">      <span class="attr">password:</span> <span class="string">root</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># mybatis 配置内容</span></span><br><span class="line"><span class="comment">#mybatis:</span></span><br><span class="line"><span class="comment">#  config-location: classpath:mybatis-config.xml # 配置 MyBatis 配置文件路径</span></span><br><span class="line"><span class="comment">#  type-aliases-package: cn.javadog.labs.springboot2.datasource.dynamic.mybatis.dataobject # 配置数据库实体包路径</span></span><br></pre></td></tr></table></figure><ul><li>在 <code>spring.datasource</code> 配置项中，我们设置了 <code>orders</code> 和 <code>users</code> 两个数据源。</li><li>注释掉 <code>mybatis</code> 配置项，因为我们不使用 <code>mybatis-spring-boot-starter</code> 自动化配置 MyBatis ，而是自己写配置类，自定义配置 MyBatis 。</li></ul><h2 id="5-4-MyBatis-配置文件"><a href="#5-4-MyBatis-配置文件" class="headerlink" title="5.4 MyBatis 配置文件"></a>5.4 MyBatis 配置文件</h2><p>和 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「3.4 MyBatis 配置文件」</a> 一致。</p><h2 id="5-5-MyBatis-配置类"><a href="#5-5-MyBatis-配置类" class="headerlink" title="5.5 MyBatis 配置类"></a>5.5 MyBatis 配置类</h2><p>在 <code>cn.javadog.labs.springboot2.datasource.dynamic.mybatis.config</code> 包路径下，我们会分别创建：</p><ul><li><code>MyBatisOrdersConfig</code> 配置类，配置使用 <code>orders</code> 数据源的 MyBatis 配置。</li><li><code>MyBatisUsersConfig</code> 配置类，配置使用 <code>users</code> 数据源的 MyBatis 配置。</li></ul><p>两个 MyBatis 配置类代码是一致的，只是部分配置项的值不同。所以我们仅仅来看下 MyBatisOrdersConfig 配置类，而 MyBatisUsersConfig 配置类胖友自己看看即可。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// MyBatisOrdersConfig.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@MapperScan</span>(basePackages = <span class="string">"cn.javadog.labs.springboot2.datasource.dynamic.mybatis.mapper.orders"</span>, sqlSessionTemplateRef = <span class="string">"ordersSqlSessionTemplate"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyBatisOrdersConfig</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 创建 orders 数据源</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span>(name = <span class="string">"ordersDataSource"</span>)</span><br><span class="line">    <span class="meta">@ConfigurationProperties</span>(prefix = <span class="string">"spring.datasource.orders"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> DataSource <span class="title">dataSource</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> DataSourceBuilder.create().build();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 创建 MyBatis SqlSessionFactory</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span>(name = <span class="string">"ordersSqlSessionFactory"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> SqlSessionFactory <span class="title">sqlSessionFactory</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        SqlSessionFactoryBean bean = <span class="keyword">new</span> SqlSessionFactoryBean();</span><br><span class="line">        <span class="comment">// &lt;2.1&gt; 设置 orders 数据源</span></span><br><span class="line">        bean.setDataSource(<span class="keyword">this</span>.dataSource());</span><br><span class="line">        <span class="comment">// &lt;2.2&gt; 设置 entity 所在包</span></span><br><span class="line">  			bean.setTypeAliasesPackage( <span class="string">"cn.javadog.labs.springboot2.datasource.dynamic.mybatis.dataobject"</span>);</span><br><span class="line">        <span class="comment">// &lt;2.3&gt; 设置 config 路径</span></span><br><span class="line">        bean.setConfigLocation(<span class="keyword">new</span> PathMatchingResourcePatternResolver().getResource(<span class="string">"classpath:mybatis-config.xml"</span>));</span><br><span class="line">        <span class="comment">// &lt;2.4&gt; 设置 mapper 路径</span></span><br><span class="line">        bean.setMapperLocations(<span class="keyword">new</span> PathMatchingResourcePatternResolver().getResources(<span class="string">"classpath:mapper/orders/*.xml"</span>));</span><br><span class="line">        <span class="keyword">return</span> bean.getObject();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 创建 MyBatis SqlSessionTemplate</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span>(name = <span class="string">"ordersSqlSessionTemplate"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> SqlSessionTemplate <span class="title">sqlSessionTemplate</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> SqlSessionTemplate(<span class="keyword">this</span>.sqlSessionFactory());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 创建 orders 数据源的 TransactionManager 事务管理器</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span>(name = DBConstants.TX_MANAGER_ORDERS)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> PlatformTransactionManager <span class="title">transactionManager</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> DataSourceTransactionManager(<span class="keyword">this</span>.dataSource());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p><code>#dataSource()</code> 方法，创建 <code>orders</code> 数据源。</p></li><li><p><code>#sqlSessionFactory()</code> 方法，创建 MyBatis SqlSessionFactory Bean 。</p><ul><li><code>&lt;2.1&gt;</code> 处，设置 <code>orders</code> 数据源。</li><li><code>&lt;2.2&gt;</code> 处，设置 <code>entity</code> 所在包，作为类型别名。</li></ul></li><li><p><code>&lt;2.3&gt;</code> 处，设置 <code>config</code> 路径，这里我们使用 <code>classpath:mybatis-config.xml&quot;</code> 配置文件。</p><ul><li><code>&lt;2.4&gt;</code> 处，设置 Mapper 路径，这里我们使用 <code>classpath:mapper/orders/*.xml</code> 。我们将 <code>resource/mapper</code> 路径下，拆分为 <code>orders</code> 路径下的 Mapper XML 用于 <code>orders</code> 数据源，<code>users</code> 路径下的 Mapper XML 用于 <code>users</code> 数据源。</li></ul></li><li><p>通过上述设置，我们就创建出使用 <code>orders</code> 数据源的 SqlSessionFactory Bean 对象。</p></li><li><p><code>#sqlSessionTemplate()</code> 方法，创建 MyBatis SqlSessionTemplate Bean 。其内部的 <code>sqlSessionFactory</code> 使用的就是对应 <code>orders</code> 数据源的 SqlSessionFactory 对象。</p></li><li><p>在类上，有<code>@MapperScan</code> 注解：</p><ul><li>配置 <code>basePackages</code> 属性，它会扫描 <code>cn.javadog.labs.springboot2.datasource.dynamic.mybatis.mapper.orders</code>包下的 <code>orders</code> 包下的 Mapper 接口。和 <code>resource/mapper</code> 路径一样，我们也将 <code>mapper</code> 包路径，拆分为 <code>orders</code> 包下的 Mapper 接口用于 <code>orders</code> 数据源，<code>users</code> 包下的 Mapper 接口用于 <code>users</code> 数据源。</li></ul></li><li><p>配置 <code>sqlSessionTemplateRef</code> 属性，它会使用 <code>#sqlSessionTemplate()</code> 方法创建的 SqlSessionTemplate Bean 对象。</p><ul><li>这样，我们就能保证 <code>cn.javadog.labs.springboot2.datasource.dynamic.mybatis.mapper.orders</code> 下的 Mapper 使用的是操作 <code>orders</code> 数据源的 SqlSessionFactory ，从而操作 <code>orders</code> 数据源。</li></ul></li><li><p><code>#transactionManager()</code> 方法，创建 <code>orders</code> 数据源的 Spring 事务管理器。因为，我们项目中，一般使用 Spring 管理事务。另外，我们在 <code>DBConstants.java</code> 枚举了 <code>TX_MANAGER_ORDERS</code> 和 <code>TX_MANAGER_USERS</code> 两个事务管理器的名字。</p></li></ul><blockquote><p>卡姆：相比来说，这种方式会相对繁琐。但是如果项目中大量采用，可以封装自己的 Spring Boot Starter ，以实现自动化配置。</p></blockquote><h2 id="5-6-实体类"><a href="#5-6-实体类" class="headerlink" title="5.6 实体类"></a>5.6 实体类</h2><p>和 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「3.5 实体类」</a> 一致。</p><h2 id="5-7-Mapper"><a href="#5-7-Mapper" class="headerlink" title="5.7 Mapper"></a>5.7 Mapper</h2><p>和 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「3.6 Mapper」</a> 基本一致，差别在于分出了 <code>orders</code> 和 <code>users</code> 两个。具体看如下两个传送门：</p><ul><li><a href="https://github.com/yuyong725/labs-springboot2/tree/master/labs-springboot2-data-access/labs-springboot2-datasource-dynamic/labs-springboot2-datasource-dynamic-mybatis/src/main/java/cn/javadog/labs/springboot2/datasource/dynamic/mybatis/mapper" target="_blank" rel="noopener">Mapper 接口</a></li><li><a href="https://github.com/yuyong725/labs-springboot2/blob/master/labs-springboot2-data-access/labs-springboot2-datasource-dynamic/labs-springboot2-datasource-dynamic-mybatis/src/main/resources/mapper" target="_blank" rel="noopener">Mapper XML</a></li></ul><h2 id="5-8-简单测试"><a href="#5-8-简单测试" class="headerlink" title="5.8 简单测试"></a>5.8 简单测试</h2><p>创建 <code>UserMapperTest</code> 和 <code>OrderMapperTest</code> 测试类，我们来测试一下简单的 UserMapper 和 OrderMapper 的每个操作。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// OrderMapperTest.java</span></span><br><span class="line"><span class="meta">@RunWith</span>(SpringRunner<span class="class">.<span class="keyword">class</span>)</span></span><br><span class="line"><span class="class">@<span class="title">SpringBootTest</span>(<span class="title">classes</span> </span>= Application<span class="class">.<span class="keyword">class</span>)</span></span><br><span class="line"><span class="class"><span class="title">public</span> <span class="title">class</span> <span class="title">OrderMapperTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> OrderMapper orderMapper;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testSelectById</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        OrderDO order = orderMapper.selectById(<span class="number">1</span>);</span><br><span class="line">        System.out.println(order);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// UserMapperTest.java</span></span><br><span class="line"><span class="meta">@RunWith</span>(SpringRunner<span class="class">.<span class="keyword">class</span>)</span></span><br><span class="line"><span class="class">@<span class="title">SpringBootTest</span>(<span class="title">classes</span> </span>= Application<span class="class">.<span class="keyword">class</span>)</span></span><br><span class="line"><span class="class"><span class="title">public</span> <span class="title">class</span> <span class="title">UserMapperTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserMapper userMapper;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testSelectById</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        UserDO user = userMapper.selectById(<span class="number">1</span>);</span><br><span class="line">        System.out.println(user);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>胖友自己跑下测试用例。如果跑通，说明配置就算成功了。</p><h2 id="5-9-详细测试"><a href="#5-9-详细测试" class="headerlink" title="5.9 详细测试"></a>5.9 详细测试</h2><p>在本小节，我们会编写 4 个测试用例，尝试方案二【不同操作类，固定数据源】存在的情况。</p><p>在 <code>cn.javadog.labs.springboot2.datasource.dynamic.mybatis.service</code>包路径下，创建 <code>OrderService.java</code>类。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// OrderService.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">OrderService</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> OrderMapper orderMapper;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserMapper userMapper;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> OrderService <span class="title">self</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> (OrderService) AopContext.currentProxy();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">method01</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// ... 省略代码</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Transactional</span> <span class="comment">// 报错，找不到事务管理器</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">method02</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// ... 省略代码</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">method03</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// ... 省略代码</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Transactional</span>(transactionManager = DBConstants.TX_MANAGER_ORDERS)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">method05</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 查询订单</span></span><br><span class="line">        OrderDO order = orderMapper.selectById(<span class="number">1</span>);</span><br><span class="line">        System.out.println(order);</span><br><span class="line">        <span class="comment">// 查询用户</span></span><br><span class="line">        self().method052();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>每个测试场景，和 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「3.8 详细测试」</a> 的测试场景是相对应的，按照编号。</li><li>每一个 <code>#methodXX()</code> 方法，都代表一个测试用例，胖友可以使用 <code>OrderServiceTest</code> 进行测试。</li></ul><p>下面，我们来一个一个看。</p><p><strong>场景一：<code>#method01()</code></strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// OrderService.java</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">method01</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 查询订单</span></span><br><span class="line">    OrderDO order = orderMapper.selectById(<span class="number">1</span>);</span><br><span class="line">    System.out.println(order);</span><br><span class="line">    <span class="comment">// 查询用户</span></span><br><span class="line">    UserDO user = userMapper.selectById(<span class="number">1</span>);</span><br><span class="line">    System.out.println(user);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>方法未使用 <code>@Transactional</code> 注解，不会开启事务。</li><li>对于 OrderMapper 和 UserMapper 的查询操作，分别使用其接口对应的 SqlSessionTemplate ，找到对应的数据源，执行操作。</li><li>这样一看，在未开启事务的情况下，我们已经能够自由的使用多数据源落。</li></ul><p><strong>场景二：<code>#method02()</code></strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// OrderService.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Transactional</span> <span class="comment">// 报错，找不到事务管理器</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">method02</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 查询订单</span></span><br><span class="line">    OrderDO order = orderMapper.selectById(<span class="number">1</span>);</span><br><span class="line">    System.out.println(order);</span><br><span class="line">    <span class="comment">// 查询用户</span></span><br><span class="line">    UserDO user = userMapper.selectById(<span class="number">1</span>);</span><br><span class="line">    System.out.println(user);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>和 <code>#method02()</code> 方法，差异在于，方法上增加了 <code>@Transactional</code> 注解，声明要使用 Spring 事务。</p></li><li><p>执行方法，抛出如下异常：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">NoUniqueBeanDefinitionException: No qualifying bean of type &#39;org.springframework.transaction.PlatformTransactionManager&#39; available: expected single matching bean but found 2: ordersTransactionManager,usersTransactionManager</span><br></pre></td></tr></table></figure><ul><li>在 <code>@Transactional</code> 注解上，如果未设置使用的事务管理器，它会去选择一个事务管理器。但是，我们这里创建了 <code>ordersTransactionManager</code> 和 <code>usersTransactionManager</code> 两个事务管理器，它就不知道怎么选了。此时，它只好抛出 NoUniqueBeanDefinitionException 异常。</li></ul></li></ul><p><strong>场景三：<code>#method03()</code></strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// OrderService.java</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">method03</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 查询订单</span></span><br><span class="line">    self().method031();</span><br><span class="line">    <span class="comment">// 查询用户</span></span><br><span class="line">    self().method032();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Transactional</span>(transactionManager = DBConstants.TX_MANAGER_ORDERS)</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">method031</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    OrderDO order = orderMapper.selectById(<span class="number">1</span>);</span><br><span class="line">    System.out.println(order);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Transactional</span>(transactionManager = DBConstants.TX_MANAGER_USERS)</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">method032</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    UserDO user = userMapper.selectById(<span class="number">1</span>);</span><br><span class="line">    System.out.println(user);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>执行方法，正常结束，未抛出异常。</li><li><code>#method031()</code> 和 <code>#method032()</code> 方法上，声明的事务管理器，和后续 Mapper 操作是同一个 DataSource 数据源，从而保证不报错。</li></ul><p><strong>场景四：<code>#method05()</code></strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// OrderService.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Transactional</span>(transactionManager = DBConstants.TX_MANAGER_ORDERS)</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">method05</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 查询订单</span></span><br><span class="line">    OrderDO order = orderMapper.selectById(<span class="number">1</span>);</span><br><span class="line">    System.out.println(order);</span><br><span class="line">    <span class="comment">// 查询用户</span></span><br><span class="line">    self().method052();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Transactional</span>(transactionManager = DBConstants.TX_MANAGER_USERS,</span><br><span class="line">        propagation = Propagation.REQUIRES_NEW)</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">method052</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    UserDO user = userMapper.selectById(<span class="number">1</span>);</span><br><span class="line">    System.out.println(user);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>执行方法，正常结束，未抛出异常。</p></li><li><p>我们仔细看看 <code>#method052()</code> 方法，我们添加的 <code>@Transactionl</code> 注解，使用的事务传播级别是</p><p><code>Propagation.REQUIRES_NEW</code> 。此时，在执行 <code>#method052()</code> 方法之前，TransactionInterceptor 会将原事务挂起，暂时性的将原事务信息和当前线程解绑。</p><ul><li>所以，在执行 <code>#method052()</code> 方法前，又可以执行一轮事务的逻辑。</li><li>之后，在执行 <code>#method052()</code> 方法完成后，会将原事务<strong>恢复</strong>，重新将原事务信息和当前线程绑定。</li></ul></li><li><p>编写这个场景的目的，是想告诉胖友，如果在使用方案二【不同操作类，固定数据源】，在事务中时，如何切换数据源。当然，一旦切换数据源，可能产生多个事务，就会碰到多个事务一致性的问题，也就是分布式事务。</p></li></ul><p>四个场景，胖友在好好理解。可以尝试调试下源码，更好的帮助理解。</p><p>咳咳咳，如果有解释不到位的地方，欢迎胖友留言。</p><h2 id="5-10-读写分离"><a href="#5-10-读写分离" class="headerlink" title="5.10 读写分离"></a>5.10 读写分离</h2><p>按照这个思路，如果想要实现 MyBatis 读写分离。还是类似的思路。只是将<strong>从库</strong>作为一个“<strong>特殊</strong>”的数据源，需要做的是：</p><ul><li>应用配置文件增加<strong>从库</strong>的数据源。</li><li>增加一套<strong>从库</strong>的 MyBatis 配置类。</li><li>增加一套<strong>从库</strong>相关的 MyBatis Mapper 接口、Mapper XML 文件。</li></ul><p>相比方案一【基于 Spring AbstractRoutingDataSource 做拓展】来说，更加麻烦。并且，万一有多从呢？嘿嘿。</p><p>所以呢，实际项目在选型时，方案一会<strong>优</strong>于方案二，被更普遍的采用。</p><h1 id="6-Spring-Data-JPA-多数据源"><a href="#6-Spring-Data-JPA-多数据源" class="headerlink" title="6. Spring Data JPA 多数据源"></a>6. Spring Data JPA 多数据源</h1><blockquote><p>示例代码对应仓库：<a href="https://github.com/yuyong725/labs-springboot2/tree/master/labs-springboot2-data-access/labs-springboot2-jpa" target="_blank" rel="noopener">labs-springboot2-jpa</a> 。</p></blockquote><p>本小节，我们会基于方案二【不同操作类，固定数据源】的方式，实现 Spring Data JPA 多数据源。</p><blockquote><p>卡姆：整个过程，和 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「5. MyBatis 多数据源」</a> 是类似的，所以讲解会想对精简一些。</p></blockquote><p>如果胖友对 Spring Data JPA 不了解的话，可以看看 <a href="https://calm_java.gitee.io/blog/2020/01/08/labs-springboot2-jpa/">《Spring Boot JPA 入门》》</a> 文章。</p><h2 id="6-1-引入依赖"><a href="#6-1-引入依赖" class="headerlink" title="6.1 引入依赖"></a>6.1 引入依赖</h2><p>在 <code>pom.xml</code> 文件中，引入相关依赖。</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">project</span> <span class="attr">xmlns</span>=<span class="string">"http://maven.apache.org/POM/4.0.0"</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xmlns:xsi</span>=<span class="string">"http://www.w3.org/2001/XMLSchema-instance"</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xsi:schemaLocation</span>=<span class="string">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>labs-springboot2-datasource-dynamic<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>cn.javadog<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.0-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">modelVersion</span>&gt;</span>4.0.0<span class="tag">&lt;/<span class="name">modelVersion</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>labs-springboot2-datasource-dynamic-jpa<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 实现对数据库连接池的自动化配置 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-jdbc<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 本示例，我们使用 MySQL --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>mysql<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>mysql-connector-java<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- JPA 相关依赖 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-data-jpa<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 方便等会写单元测试 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-test<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">scope</span>&gt;</span>test<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br></pre></td></tr></table></figure><ul><li>具体每个依赖的作用，胖友自己认真看下添加的所有注释噢。</li><li>对于 <code>spring-boot-starter-data-jpa</code> 依赖，这里并不使用它实现对 JPA 的自动化配置。这么引入，只是单纯方便，不然需要引入 <a href="https://mvnrepository.com/artifact/org.springframework.data/spring-data-jpa" target="_blank" rel="noopener">spring-data-jpa</a> 和 <a href="https://mvnrepository.com/artifact/org.hibernate/hibernate-core" target="_blank" rel="noopener">hibernate-core</a> 等等依赖。</li></ul><h2 id="6-2-Application"><a href="#6-2-Application" class="headerlink" title="6.2 Application"></a>6.2 Application</h2><p>创建 <code>Application.java</code>类，代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@EnableAspectJAutoProxy</span>(exposeProxy = <span class="keyword">true</span>) <span class="comment">// http://www.voidcn.com/article/p-zddcuyii-bpt.html</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Application</span> </span>&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="6-3-应用配置文件"><a href="#6-3-应用配置文件" class="headerlink" title="6.3 应用配置文件"></a>6.3 应用配置文件</h2><p>在 <code>resources</code> 目录下，创建 <code>application.yaml</code> 配置文件。配置如下：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">datasource:</span></span><br><span class="line">    <span class="comment"># 订单 orders 数据源配置</span></span><br><span class="line">    <span class="attr">orders:</span></span><br><span class="line">      <span class="attr">jdbc-url:</span> <span class="string">jdbc:mysql://127.0.0.1:3306/labs-springboot2-multiple?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai</span></span><br><span class="line">      <span class="attr">driver-class-name:</span> <span class="string">com.mysql.cj.jdbc.Driver</span></span><br><span class="line">      <span class="attr">username:</span> <span class="string">root</span></span><br><span class="line">      <span class="attr">password:</span> <span class="string">root</span></span><br><span class="line">    <span class="comment"># 用户 users 数据源配置</span></span><br><span class="line">    <span class="attr">users:</span></span><br><span class="line">      <span class="attr">jdbc-url:</span> <span class="string">jdbc:mysql://127.0.0.1:3306/labs-springboot2?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai</span></span><br><span class="line">      <span class="attr">driver-class-name:</span> <span class="string">com.mysql.cj.jdbc.Driver</span></span><br><span class="line">      <span class="attr">username:</span> <span class="string">root</span></span><br><span class="line">      <span class="attr">password:</span> <span class="string">root</span></span><br><span class="line"></span><br><span class="line">  <span class="attr">jpa:</span></span><br><span class="line">    <span class="attr">show-sql:</span> <span class="literal">true</span> <span class="comment"># 打印 SQL 。生产环境，建议关闭</span></span><br><span class="line">    <span class="comment"># Hibernate 配置内容，对应 HibernateProperties 类</span></span><br><span class="line">    <span class="attr">hibernate:</span></span><br><span class="line">      <span class="attr">ddl-auto:</span> <span class="string">none</span></span><br></pre></td></tr></table></figure><ul><li>在 <code>spring.datasource</code> 配置项中，我们设置了 <code>orders</code> 和 <code>users</code> 两个数据源。</li></ul><h2 id="6-4-Spring-Data-JPA-配置类"><a href="#6-4-Spring-Data-JPA-配置类" class="headerlink" title="6.4 Spring Data JPA 配置类"></a>6.4 Spring Data JPA 配置类</h2><p>在 <code>cn.javadog.labs.springboot2.datasource.dynamic.jpa.config</code>包路径下，创建 <code>HibernateConfig.java</code> 配置类。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// HibernateConfig.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">HibernateConfig</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> JpaProperties jpaProperties;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> HibernateProperties hibernateProperties;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取 Hibernate Vendor 相关配置</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span>(name = <span class="string">"hibernateVendorProperties"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> Map&lt;String, Object&gt; <span class="title">hibernateVendorProperties</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> hibernateProperties.determineHibernateProperties(</span><br><span class="line">                jpaProperties.getProperties(), <span class="keyword">new</span> HibernateSettings());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>目的是获得 Hibernate Vendor 相关配置。不用纠结它是什么，知道需要获得即可。</li></ul><p>在 <code>cn.javadog.labs.springboot2.datasource.dynamic.jpa.config</code>包路径下，我们会分别创建：</p><ul><li><code>JpaOrdersConfig</code> 配置类，配置使用 <code>orders</code> 数据源的 Spring Data JPA 配置。</li><li><code>JpaUsersConfig</code> 配置类，配置使用 <code>users</code> 数据源的 Spring Data JPA 配置。</li></ul><p>两个 Spring Data JPA 配置类代码是一致的，只是部分配置项的值不同。所以我们仅仅来看下 JpaOrdersConfig 配置类，而 JpaUsersConfig 配置类胖友自己看看即可。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// JpaOrdersConfig.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@EnableJpaRepositories</span>(</span><br><span class="line">        entityManagerFactoryRef = DBConstants.ENTITY_MANAGER_FACTORY_ORDERS,</span><br><span class="line">        transactionManagerRef = DBConstants.TX_MANAGER_ORDERS,</span><br><span class="line">        basePackages = &#123;<span class="string">"cn.javadog.labs.springboot2.datasource.dynamic.jpa.repository.orders"</span>&#125;) <span class="comment">// 设置 Repository 接口所在包</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">JpaOrdersConfig</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Resource</span>(name = <span class="string">"hibernateVendorProperties"</span>)</span><br><span class="line">    <span class="keyword">private</span> Map&lt;String, Object&gt; hibernateVendorProperties;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 创建 orders 数据源</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span>(name = <span class="string">"ordersDataSource"</span>)</span><br><span class="line">    <span class="meta">@ConfigurationProperties</span>(prefix = <span class="string">"spring.datasource.orders"</span>)</span><br><span class="line">    <span class="meta">@Primary</span> <span class="comment">// 需要特殊添加，否则初始化会有问题</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> DataSource <span class="title">dataSource</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> DataSourceBuilder.create().build();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 创建 LocalContainerEntityManagerFactoryBean</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span>(name = DBConstants.ENTITY_MANAGER_FACTORY_ORDERS)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> LocalContainerEntityManagerFactoryBean <span class="title">entityManagerFactory</span><span class="params">(EntityManagerFactoryBuilder builder)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> builder</span><br><span class="line">                .dataSource(<span class="keyword">this</span>.dataSource()) <span class="comment">// 数据源</span></span><br><span class="line">                .properties(hibernateVendorProperties) <span class="comment">// 获取并注入 Hibernate Vendor 相关配置</span></span><br><span class="line">                .packages(<span class="string">"cn.javadog.labs.springboot2.datasource.dynamic.jpa.dataobject"</span>) <span class="comment">// 数据库实体 entity 所在包</span></span><br><span class="line">                .persistenceUnit(<span class="string">"ordersPersistenceUnit"</span>) <span class="comment">// 设置持久单元的名字，需要唯一</span></span><br><span class="line">                .build();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 创建 PlatformTransactionManager</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span>(name = DBConstants.TX_MANAGER_ORDERS)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> PlatformTransactionManager <span class="title">transactionManager</span><span class="params">(EntityManagerFactoryBuilder builder)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> JpaTransactionManager(entityManagerFactory(builder).getObject());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p><code>#dataSource()</code> 方法，创建 <code>orders</code> 数据源。</p></li><li><p><code>#entityManagerFactoryPrimary(EntityManagerFactoryBuilder builder)</code>方法，创建 LocalContainerEntityManagerFactoryBean Bean ，它是创建 EntityManager 实体管理器的工厂 Bean ，</p><p>最终会创建对应的 EntityManager Bean。</p><ul><li><code>&lt;2.1&gt;</code> 处，设置使用的数据源是 <code>orders</code> 。</li></ul></li><li><p><code>&lt;2.2&gt;</code> 处，设置 Hibernate Vendor 相关配置。</p><ul><li><code>&lt;2.3&gt;</code> 处，设置数据库实体 Entity 所在包。</li></ul></li><li><p><code>&lt;2.4&gt;</code> 处，设置持久单元的名字，需要唯一。</p></li><li><p><code>#transactionManager(EntityManagerFactoryBuilder builder)</code> 方法，创建使用上述 EntityManager 的 JpaTransactionManager Bean 对象。这样，该事务管理器使用的也是 <code>orders</code> 数据源。</p></li><li><p>最终，通过 <code>@EnableJpaRepositories</code>注解，串联在一起：</p><ul><li><code>entityManagerFactoryRef</code> 属性，保证了使用 <code>orders</code> 数据源的 EntityManager 实体管理器的工厂 Bean 。</li></ul></li><li><p><code>transactionManagerRef</code> 属性，保证了使用 <code>orders</code> 数据源的 PlatformTransactionManager 事务管理器 Bean 。</p><ul><li><code>basePackages</code> 属性，它会扫描 <code>cn.javadog.labs.springboot2.datasource.dynamic.jpa.repository.orders</code>包下的 <code>orders</code> 包下的 Repository 接口。我们将 <code>repository</code> 包路径，拆分为 <code>orders</code> 包下的 Repository 接口用于 <code>orders</code> 数据源，<code>users</code> 包下的 Repository 接口用于 <code>users</code> 数据源。</li></ul></li><li><p>另外，我们在 <code>DBConstants.java</code> 类中，枚举了：</p><ul><li><code>TX_MANAGER_ORDERS</code> 和 <code>TX_MANAGER_USERS</code> 两个事务管理器的名字，方便代码中使用。</li></ul></li><li><p><code>ENTITY_MANAGER_FACTORY_ORDERS</code> 和 <code>ENTITY_MANAGER_FACTORY_USERS</code> 两个实体管理器的名字。</p></li></ul><blockquote><p>卡姆：相比来说，这种方式会相对繁琐。但是如果项目中大量采用，可以封装自己的 Spring Boot Starter ，以实现自动化配置。</p></blockquote><h2 id="6-5-实体类"><a href="#6-5-实体类" class="headerlink" title="6.5 实体类"></a>6.5 实体类</h2><p>和 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「3.5 实体类」</a> 基本一致，差别在于增加了 JPA 相关注解。具体看如下两个传送门：</p><ul><li><a href="https://github.com/yuyong725/labs-springboot2/blob/master/labs-springboot2-data-access/labs-springboot2-datasource-dynamic/labs-springboot2-datasource-dynamic-jpa/src/main/java/cn/javadog/labs/springboot2/datasource/dynamic/jpa/dataobject/OrderDO.java" target="_blank" rel="noopener">OrderDO.java</a></li><li><a href="https://github.com/yuyong725/labs-springboot2/blob/master/labs-springboot2-data-access/labs-springboot2-datasource-dynamic/labs-springboot2-datasource-dynamic-jpa/src/main/java/cn/javadog/labs/springboot2/datasource/dynamic/jpa/dataobject/UserDO.java" target="_blank" rel="noopener">UserDO.java</a></li></ul><h2 id="6-6-Repository"><a href="#6-6-Repository" class="headerlink" title="6.6 Repository"></a>6.6 Repository</h2><p>和 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「3.6 Mapper」</a> 基本一致，差别在于使用 Spring Data Repository 接口。具体看如下两个传送门：</p><ul><li><a href="https://github.com/yuyong725/labs-springboot2/blob/master/labs-springboot2-data-access/labs-springboot2-datasource-dynamic/labs-springboot2-datasource-dynamic-jpa/src/main/java/cn/javadog/labs/springboot2/datasource/dynamic/jpa/repository/orders/OrderRepository.java" target="_blank" rel="noopener">OrderRepository</a></li><li><a href="https://github.com/yuyong725/labs-springboot2/blob/master/labs-springboot2-data-access/labs-springboot2-datasource-dynamic/labs-springboot2-datasource-dynamic-jpa/src/main/java/cn/javadog/labs/springboot2/datasource/dynamic/jpa/repository/users/UserRepository.java" target="_blank" rel="noopener">UserRepository</a></li></ul><h2 id="6-7-简单测试"><a href="#6-7-简单测试" class="headerlink" title="6.7 简单测试"></a>6.7 简单测试</h2><p>和 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「5.8 简单测试」</a> 基本一致，具体看如下两个传送门：</p><ul><li><a href="https://github.com/yuyong725/labs-springboot2/blob/master/labs-springboot2-data-access/labs-springboot2-datasource-dynamic/labs-springboot2-datasource-dynamic-jpa/src/test/java/cn/javadog/labs/springboot2/datasource/dynamic/jpa/OrderRepositoryTest.java" target="_blank" rel="noopener">OrderRepositoryTest</a></li><li><a href="https://github.com/yuyong725/labs-springboot2/blob/master/labs-springboot2-data-access/labs-springboot2-datasource-dynamic/labs-springboot2-datasource-dynamic-jpa/src/test/java/cn/javadog/labs/springboot2/datasource/dynamic/jpa/UserRepositoryTest.java" target="_blank" rel="noopener">UserRepositoryTest</a></li></ul><h2 id="6-8-详细测试"><a href="#6-8-详细测试" class="headerlink" title="6.8 详细测试"></a>6.8 详细测试</h2><p>和 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「5.9 详细测试」</a> 基本一致，具体看如下两个传送门：</p><ul><li><a href="https://github.com/yuyong725/labs-springboot2/blob/master/labs-springboot2-data-access/labs-springboot2-datasource-dynamic/labs-springboot2-datasource-dynamic-jpa/src/main/java/cn/javadog/labs/springboot2/datasource/dynamic/jpa/service/OrderService.java" target="_blank" rel="noopener">OrderService</a></li><li><a href="https://github.com/yuyong725/labs-springboot2/blob/master/labs-springboot2-data-access/labs-springboot2-datasource-dynamic/labs-springboot2-datasource-dynamic-jpa/src/test/java/cn/javadog/labs/springboot2/datasource/dynamic/jpa/OrderServiceTest.java" target="_blank" rel="noopener">OrderServiceTest</a></li></ul><h2 id="6-9-读写分离"><a href="#6-9-读写分离" class="headerlink" title="6.9 读写分离"></a>6.9 读写分离</h2><p>和 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「5.10 读写分离」</a> <strong>思路</strong>基本一致。</p><h1 id="7-JdbcTemplate-多数据源"><a href="#7-JdbcTemplate-多数据源" class="headerlink" title="7. JdbcTemplate 多数据源"></a>7. JdbcTemplate 多数据源</h1><blockquote><p>示例代码对应仓库：<a href="https://github.com/yuyong725/labs-springboot2/tree/master/labs-springboot2-data-access/labs-springboot2-datasource-dynamic/labs-springboot2-datasource-dynamic-jdbctemplate" target="_blank" rel="noopener">labs-springboot2-datasource-dynamic-jdbctemplate</a> 。</p></blockquote><p>本小节，我们会基于方案二【不同操作类，固定数据源】的方式，实现 Spring JdbcTemplate 多数据源。</p><blockquote><p>卡姆：整个过程，和 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「5. MyBatis 多数据源」</a> 是类似的，所以讲解会想对精简一些。</p><p>内心 OS ：我只是想赶紧进入 Sharding-JDBC 的环节，真的不是想偷懒，哈哈哈哈。</p></blockquote><p>如果胖友对 Spring JdbcTemplate 不了解的话，可以看看 <a href="https://calm_java.gitee.io/blog/2020/01/07/labs-springboot2-jdbctemplate/">《Spring Boot JdbcTemplate 入门》》</a> 文章。</p><h2 id="7-1-引入依赖"><a href="#7-1-引入依赖" class="headerlink" title="7.1 引入依赖"></a>7.1 引入依赖</h2><p>在 <code>pom.xml</code>]文件中，引入相关依赖。</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">project</span> <span class="attr">xmlns</span>=<span class="string">"http://maven.apache.org/POM/4.0.0"</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xmlns:xsi</span>=<span class="string">"http://www.w3.org/2001/XMLSchema-instance"</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xsi:schemaLocation</span>=<span class="string">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>labs-springboot2-datasource-dynamic<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>cn.javadog<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.0-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">modelVersion</span>&gt;</span>4.0.0<span class="tag">&lt;/<span class="name">modelVersion</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>labs-springboot2-datasource-dynamic-jdbctemplate<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 实现对数据库连接池的自动化配置 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-jdbc<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 本示例，我们使用 MySQL --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>mysql<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>mysql-connector-java<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 保证 Spring AOP 相关的依赖包 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-aspects<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 方便等会写单元测试 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-test<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">scope</span>&gt;</span>test<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br></pre></td></tr></table></figure><p>具体每个依赖的作用，胖友自己认真看下添加的所有注释噢。</p><h2 id="7-2-Application"><a href="#7-2-Application" class="headerlink" title="7.2 Application"></a>7.2 Application</h2><p>创建 <code>Application.java</code> 类，代码如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">@SpringBootApplication</span><br><span class="line">@EnableAspectJAutoProxy(exposeProxy &#x3D; true) &#x2F;&#x2F; http:&#x2F;&#x2F;www.voidcn.com&#x2F;article&#x2F;p-zddcuyii-bpt.html</span><br><span class="line">public class Application &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="7-3-应用配置文件"><a href="#7-3-应用配置文件" class="headerlink" title="7.3 应用配置文件"></a>7.3 应用配置文件</h2><p>在 <code>resources</code> 目录下，创建 <code>application.yaml</code> 配置文件。配置如下：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="comment"># datasource 数据源配置内容</span></span><br><span class="line">  <span class="attr">datasource:</span></span><br><span class="line">    <span class="comment"># 订单数据源配置</span></span><br><span class="line">    <span class="attr">orders:</span></span><br><span class="line">      <span class="attr">jdbc-url:</span> <span class="string">jdbc:mysql://127.0.0.1:3306/labs-springboot2-multiple?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai</span></span><br><span class="line">      <span class="attr">driver-class-name:</span> <span class="string">com.mysql.cj.jdbc.Driver</span></span><br><span class="line">      <span class="attr">username:</span> <span class="string">root</span></span><br><span class="line">      <span class="attr">password:</span> <span class="string">root</span></span><br><span class="line">    <span class="comment"># 用户 users 数据源配置</span></span><br><span class="line">    <span class="attr">users:</span></span><br><span class="line">      <span class="attr">jdbc-url:</span> <span class="string">jdbc:mysql://127.0.0.1:3306/labs-springboot2?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai</span></span><br><span class="line">      <span class="attr">driver-class-name:</span> <span class="string">com.mysql.cj.jdbc.Driver</span></span><br><span class="line">      <span class="attr">username:</span> <span class="string">root</span></span><br><span class="line">      <span class="attr">password:</span> <span class="string">root</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#logging:</span></span><br><span class="line"><span class="comment">#  level:</span></span><br><span class="line"><span class="comment">#    root: debug</span></span><br></pre></td></tr></table></figure><ul><li>在 <code>spring.datasource</code> 配置项中，我们设置了 <code>orders</code> 和 <code>users</code> 两个数据源。</li></ul><h2 id="7-4-JdbcTemplate-配置类"><a href="#7-4-JdbcTemplate-配置类" class="headerlink" title="7.4 JdbcTemplate 配置类"></a>7.4 JdbcTemplate 配置类</h2><p>在 <code>cn.javadog.labs.springboot2.datasource.dynamic.jdbctemplate.config</code>包路径下，我们会分别创建：</p><ul><li><code>JdbcTemplateOrdersConfig</code>配置类，配置使用 <code>orders</code> 数据源的 MyBatis 配置。</li><li><code>JdbcTemplateUsersConfig</code> 配置类，配置使用 <code>users</code> 数据源的 MyBatis 配置。</li></ul><p>两个 JdbcTemplate 配置类代码是一致的，只是部分配置项的值不同。所以我们仅仅来看下 JdbcTemplateOrdersConfig 配置类，而 JdbcTemplateUsersConfig 配置类胖友自己看看即可。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// JdbcTemplateOrdersConfig.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">JdbcTemplateOrdersConfig</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 创建 orders 数据源</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span>(name = <span class="string">"ordersDataSource"</span>)</span><br><span class="line">    <span class="meta">@ConfigurationProperties</span>(prefix = <span class="string">"spring.datasource.orders"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> DataSource <span class="title">dataSource</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> DataSourceBuilder.create().build();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 创建 orders JdbcTemplate</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span>(name = DBConstants.JDBC_TEMPLATE_ORDERS)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> JdbcTemplate <span class="title">jdbcTemplate</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> JdbcTemplate(<span class="keyword">this</span>.dataSource());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 创建 orders 数据源的 TransactionManager 事务管理器</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span>(name = DBConstants.TX_MANAGER_ORDERS)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> PlatformTransactionManager <span class="title">transactionManager</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> DataSourceTransactionManager(<span class="keyword">this</span>.dataSource());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><code>#dataSource()</code> 方法，创建 <code>orders</code> 数据源。</li><li><code>#jdbcTemplate()</code> 方法，创建使用 <code>orders</code> 数据源的 JdbcTemplate Bean 。</li><li><code>#transactionManager()</code> 方法，创建 <code>orders</code> 数据源的 Spring 事务管理器。因为，我们项目中，一般使用 Spring 管理事务。另外，我们在 <code>DBConstants.java</code> 枚举了 <code>TX_MANAGER_ORDERS</code> 和 <code>TX_MANAGER_USERS</code> 两个事务管理器的名字。</li></ul><blockquote><p>卡姆：相比来说，这种方式会相对繁琐。但是如果项目中大量采用，可以封装自己的 Spring Boot Starter ，以实现自动化配置。</p></blockquote><h2 id="7-5-实体类"><a href="#7-5-实体类" class="headerlink" title="7.5 实体类"></a>7.5 实体类</h2><p>和 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「3.5 实体类」</a> 一致。</p><h2 id="7-6-Dao"><a href="#7-6-Dao" class="headerlink" title="7.6 Dao"></a>7.6 Dao</h2><p>和 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「5.8 简单测试」</a> 基本一致，具体看如下两个传送门：</p><ul><li><a href="https://github.com/yuyong725/labs-springboot2/blob/master/labs-springboot2-data-access/labs-springboot2-datasource-dynamic/labs-springboot2-datasource-dynamic-jdbctemplate/src/main/java/cn/javadog/labs/springboot2/datasource/dynamic/jdbctemplate/dao/OrderDao.java" target="_blank" rel="noopener">OrderDao</a></li><li><a href="https://github.com/yuyong725/labs-springboot2/blob/master/labs-springboot2-data-access/labs-springboot2-datasource-dynamic/labs-springboot2-datasource-dynamic-jdbctemplate/src/main/java/cn/javadog/labs/springboot2/datasource/dynamic/jdbctemplate/dao/UserDao.java" target="_blank" rel="noopener">UserDao</a></li></ul><h2 id="7-7-简单测试"><a href="#7-7-简单测试" class="headerlink" title="7.7 简单测试"></a>7.7 简单测试</h2><p>和 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「5.8 简单测试」</a> 基本一致，具体看如下两个传送门：</p><ul><li><a href="https://github.com/yuyong725/labs-springboot2/blob/master/labs-springboot2-data-access/labs-springboot2-datasource-dynamic/labs-springboot2-datasource-dynamic-jdbctemplate/src/test/java/cn/javadog/labs/springboot2/datasource/dynamic/jdbctemplate/OrderDaoTest.java" target="_blank" rel="noopener">OrderDaoTest</a></li><li><a href="https://github.com/yuyong725/labs-springboot2/blob/master/labs-springboot2-data-access/labs-springboot2-datasource-dynamic/labs-springboot2-datasource-dynamic-jdbctemplate/src/test/java/cn/javadog/labs/springboot2/datasource/dynamic/jdbctemplate/UserDaoTest.java" target="_blank" rel="noopener">UserDaoTest</a></li></ul><h2 id="7-8-详细测试"><a href="#7-8-详细测试" class="headerlink" title="7.8 详细测试"></a>7.8 详细测试</h2><p>和 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「5.9 详细测试」</a> 基本一致，具体看如下两个传送门：</p><ul><li><a href="https://github.com/yuyong725/labs-springboot2/blob/master/labs-springboot2-data-access/labs-springboot2-datasource-dynamic/labs-springboot2-datasource-dynamic-jdbctemplate/src/main/java/cn/javadog/labs/springboot2/datasource/dynamic/jdbctemplate/service/OrderService.java" target="_blank" rel="noopener">OrderService</a></li><li><a href="https://github.com/yuyong725/labs-springboot2/blob/master/labs-springboot2-data-access/labs-springboot2-datasource-dynamic/labs-springboot2-datasource-dynamic-jdbctemplate/src/test/java/cn/javadog/labs/springboot2/datasource/dynamic/jdbctemplate/OrderServiceTest.java" target="_blank" rel="noopener">OrderServiceTest</a></li></ul><h2 id="7-9-读写分离"><a href="#7-9-读写分离" class="headerlink" title="7.9 读写分离"></a>7.9 读写分离</h2><p>和 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「5.10 读写分离」</a> <strong>思路</strong>基本一致。</p><h1 id="8-Sharding-JDBC-多数据源"><a href="#8-Sharding-JDBC-多数据源" class="headerlink" title="8. Sharding-JDBC 多数据源"></a>8. Sharding-JDBC 多数据源</h1><blockquote><p>示例代码对应仓库：<a href="https://github.com/yuyong725/labs-springboot2/tree/master/labs-springboot2-data-access/labs-springboot2-datasource-dynamic/labs-springboot2-datasource-dynamic-shardingjdbc-01" target="_blank" rel="noopener">labs-springboot2-datasource-dynamic-shardingjdbc-01</a> 。</p></blockquote><p><a href="https://github.com/apache/incubator-shardingsphere/tree/dev/sharding-jdbc" target="_blank" rel="noopener">Sharding-JDBC</a> 是 <a href="https://shardingsphere.apache.org/document/current/cn/overview/" target="_blank" rel="noopener">Apache ShardingSphere</a> 下，基于 JDBC 的分库分表组件。对于 Java 语言来说，我们推荐选择 Sharding-JDBC 优于 <a href="https://github.com/apache/incubator-shardingsphere/tree/dev/sharding-proxy" target="_blank" rel="noopener">Sharding-Proxy</a> ，主要原因是：</p><ul><li>减少一层 Proxy 的开销，性能更优。</li><li>去中心化，无需多考虑一次 Proxy 的高可用。</li></ul><p>下面，我们来使用 Sharding-JDBC 来实现多数据源。整个的示例，我们会和 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「2. baomidou 多数据源」</a> 是一样的功能，方便胖友做类比。</p><h2 id="8-1-引入依赖"><a href="#8-1-引入依赖" class="headerlink" title="8.1 引入依赖"></a>8.1 引入依赖</h2><p>在 <code>pom.xml</code> 文件中，引入相关依赖。</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">project</span> <span class="attr">xmlns</span>=<span class="string">"http://maven.apache.org/POM/4.0.0"</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xmlns:xsi</span>=<span class="string">"http://www.w3.org/2001/XMLSchema-instance"</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xsi:schemaLocation</span>=<span class="string">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>labs-springboot2-datasource-dynamic<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>cn.javadog<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.0-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">modelVersion</span>&gt;</span>4.0.0<span class="tag">&lt;/<span class="name">modelVersion</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>labs-springboot2-datasource-dynamic-shardingjdbc-01<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 实现对数据库连接池的自动化配置 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-jdbc<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 本示例，我们使用 MySQL --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>mysql<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>mysql-connector-java<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 实现对 MyBatis 的自动化配置 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.mybatis.spring.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>mybatis-spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 实现对 Sharding-JDBC 的自动化配置 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.shardingsphere<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>sharding-jdbc-spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>4.0.0-RC2<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 保证 Spring AOP 相关的依赖包 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-aspects<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 方便等会写单元测试 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-test<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">scope</span>&gt;</span>test<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br></pre></td></tr></table></figure><p>具体每个依赖的作用，胖友自己认真看下添加的所有注释噢。</p><h2 id="8-2-Application"><a href="#8-2-Application" class="headerlink" title="8.2 Application"></a>8.2 Application</h2><p>创建 <code>Application.java</code> 类，代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Application.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@MapperScan</span>(basePackages = <span class="string">"cn.javadog.labs.springboot2.datasource.dynamic.shardingjdbc.one.mapper"</span>)</span><br><span class="line"><span class="meta">@EnableAspectJAutoProxy</span>(exposeProxy = <span class="keyword">true</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Application</span> </span>&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>和 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「3.2 Application」</a> 是完全一致的。</li></ul><h2 id="8-3-应用配置文件"><a href="#8-3-应用配置文件" class="headerlink" title="8.3 应用配置文件"></a>8.3 应用配置文件</h2><p>在 <code>resources</code> 目录下，创建 <code>application.yaml</code> 配置文件。配置如下：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="comment"># ShardingSphere 配置项</span></span><br><span class="line">  <span class="attr">shardingsphere:</span></span><br><span class="line">    <span class="attr">datasource:</span></span><br><span class="line">      <span class="comment"># 所有数据源的名字</span></span><br><span class="line">      <span class="attr">names:</span> <span class="string">ds-orders,</span> <span class="string">ds-users</span></span><br><span class="line">      <span class="comment"># 订单 orders 数据源配置</span></span><br><span class="line">      <span class="attr">ds-orders:</span></span><br><span class="line">        <span class="attr">type:</span> <span class="string">com.zaxxer.hikari.HikariDataSource</span> <span class="comment"># 使用 Hikari 数据库连接池</span></span><br><span class="line">        <span class="attr">driver-class-name:</span> <span class="string">com.mysql.cj.jdbc.Driver</span></span><br><span class="line">        <span class="attr">jdbc-url:</span> <span class="string">jdbc:mysql://127.0.0.1:3306/labs-springboot2-multiple?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai</span></span><br><span class="line">        <span class="attr">username:</span> <span class="string">root</span></span><br><span class="line">        <span class="attr">password:</span> <span class="string">root</span></span><br><span class="line">      <span class="comment"># 订单 users 数据源配置</span></span><br><span class="line">      <span class="attr">ds-users:</span></span><br><span class="line">        <span class="attr">type:</span> <span class="string">com.zaxxer.hikari.HikariDataSource</span> <span class="comment"># 使用 Hikari 数据库连接池</span></span><br><span class="line">        <span class="attr">driver-class-name:</span> <span class="string">com.mysql.cj.jdbc.Driver</span></span><br><span class="line">        <span class="attr">jdbc-url:</span> <span class="string">jdbc:mysql://127.0.0.1:3306/labs-springboot2?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai</span></span><br><span class="line">        <span class="attr">username:</span> <span class="string">root</span></span><br><span class="line">        <span class="attr">password:</span> <span class="string">root</span></span><br><span class="line">    <span class="comment"># 分片规则</span></span><br><span class="line">    <span class="attr">sharding:</span></span><br><span class="line">      <span class="attr">tables:</span></span><br><span class="line">        <span class="comment"># orders 表配置</span></span><br><span class="line">        <span class="attr">dynamic_datasource_orders:</span></span><br><span class="line">          <span class="attr">actualDataNodes:</span> <span class="string">ds-orders.dynamic_datasource_orders</span> <span class="comment"># 映射到 ds-orders 数据源的 orders 表</span></span><br><span class="line">        <span class="comment"># users 表配置</span></span><br><span class="line">        <span class="attr">dynamic_datasource_users:</span></span><br><span class="line">          <span class="attr">actualDataNodes:</span> <span class="string">ds-users.dynamic_datasource_users</span> <span class="comment"># 映射到 ds-users 数据源的 users 表</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># mybatis 配置内容</span></span><br><span class="line"><span class="attr">mybatis:</span></span><br><span class="line">  <span class="attr">config-location:</span> <span class="string">classpath:mybatis-config.xml</span> <span class="comment"># 配置 MyBatis 配置文件路径</span></span><br><span class="line">  <span class="attr">mapper-locations:</span> <span class="string">classpath:mapper/*.xml</span> <span class="comment"># 配置 Mapper XML 地址</span></span><br><span class="line">  <span class="attr">type-aliases-package:</span> <span class="string">cn.javadog.labs.springboot2.datasource.dynamic.shardingjdbc.one.dataobject</span> <span class="comment"># 配置数据库实体包路径</span></span><br></pre></td></tr></table></figure><ul><li><p><code>spring.shardingsphere.datasource</code> 配置项下，我们配置了 <code>ds_orders</code> 和 <code>ds_users</code> 两个数据源。</p></li><li><blockquote><p>卡姆：这里涉及到了一些 ShardingSphere 的概念，后续胖友最好可以看看 <a href="https://shardingsphere.apache.org/document/current/cn/overview/" target="_blank" rel="noopener">官方文档</a> 。</p></blockquote></li><li><p><code>mybatis</code> 配置项，设置 <code>mybatis-spring-boot-starter</code> MyBatis 的配置内容。</p></li></ul><h2 id="8-4-MyBatis-配置文件"><a href="#8-4-MyBatis-配置文件" class="headerlink" title="8.4 MyBatis 配置文件"></a>8.4 MyBatis 配置文件</h2><p>和 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「3.4 MyBatis 配置文件」</a> 一致。</p><h2 id="8-5-实体类"><a href="#8-5-实体类" class="headerlink" title="8.5 实体类"></a>8.5 实体类</h2><p>和 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「3.5 实体类」</a> 一致。</p><h2 id="8-6-Mapper"><a href="#8-6-Mapper" class="headerlink" title="8.6 Mapper"></a>8.6 Mapper</h2><p>和 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「3.6 Mapper」</a> 一致。</p><h2 id="8-7-简单测试"><a href="#8-7-简单测试" class="headerlink" title="8.7 简单测试"></a>8.7 简单测试</h2><p>和 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「3.7 简单测试」</a> 一致。</p><h2 id="8-8-详细测试"><a href="#8-8-详细测试" class="headerlink" title="8.8 详细测试"></a>8.8 详细测试</h2><p>和 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「3.8 详细测试」</a> 代码一致，<strong>结果略有差异</strong>。</p><p>在 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「3.8 详细测试」</a> 的场景二 <code>#method02()</code> 的测试，它会抛出异常。而对于本小节使用 Sharding-JDBC 的情况下，正常跑通。这是为什么呢？</p><p>原因实际在 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「2.3 方案三」</a> 已经解释了：<strong>分库分表中间件返回的 Connection 返回的实际是动态的 DynamicRoutingConnection ，它管理了整个请求（逻辑）过程中，使用的所有的 Connection ，而最终执行 SQL 的时候，DynamicRoutingConnection 会解析 SQL ，获得表对应的真正的 Connection 执行 SQL 操作</strong>。</p><p>所以，即使在和 Spring 事务结合的时候，会通过 ThreadLocal 的方式将 Connection 和当前线程进行绑定。此时这个 Connection 也是一个 动态的 DynamicRoutingConnection 连接。</p><h1 id="9-Sharding-JDBC-读写分离"><a href="#9-Sharding-JDBC-读写分离" class="headerlink" title="9. Sharding-JDBC 读写分离"></a>9. Sharding-JDBC 读写分离</h1><blockquote><p>示例代码对应仓库：<a href="https://github.com/yuyong725/labs-springboot2/tree/master/labs-springboot2-data-access/labs-springboot2-datasource-dynamic/labs-springboot2-datasource-dynamic-shardingjdbc-02" target="_blank" rel="noopener">labs-springboot2-datasource-dynamic-shardingjdbc-02</a> 。</p></blockquote><p>Sharding-JDBC 已经提供了读写分离的支持，胖友可以看看如下两个文档：</p><ul><li><a href="https://shardingsphere.apache.org/document/current/cn/features/read-write-split/" target="_blank" rel="noopener">ShardingSphere &gt; 概念 &amp; 功能 &gt; 读写分离</a></li><li><a href="https://shardingsphere.apache.org/document/current/cn/manual/sharding-jdbc/usage/read-write-splitting/" target="_blank" rel="noopener">ShardingSphere &gt; 用户手册 &gt; Sharding-JDBC &gt; 使用手册 &gt; 读写分离</a></li></ul><p>当然，也可以先不看。</p><p>下面，我们来使用 Sharding-JDBC 来实现读写分离。整个的示例，我们会和 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「3. baomidou 读写分离」</a> 是一样的功能，方便胖友做类比。</p><h2 id="9-1-引入依赖"><a href="#9-1-引入依赖" class="headerlink" title="9.1 引入依赖"></a>9.1 引入依赖</h2><p>和 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「8.1 引入依赖」</a> 一致。</p><h2 id="9-2-Application"><a href="#9-2-Application" class="headerlink" title="9.2 Application"></a>9.2 Application</h2><p>和 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「8.2 Application」</a> 一致。</p><h2 id="9-3-应用配置文件"><a href="#9-3-应用配置文件" class="headerlink" title="9.3 应用配置文件"></a>9.3 应用配置文件</h2><p>在 <code>resources</code> 目录下，创建 <code>application.yaml</code> 配置文件。配置如下：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="comment"># ShardingSphere 配置项</span></span><br><span class="line">  <span class="attr">shardingsphere:</span></span><br><span class="line">    <span class="comment"># 数据源配置</span></span><br><span class="line">    <span class="attr">datasource:</span></span><br><span class="line">      <span class="comment"># 所有数据源的名字</span></span><br><span class="line">      <span class="attr">names:</span> <span class="string">ds-master,</span> <span class="string">ds-slave-1,</span> <span class="string">ds-slave-2</span></span><br><span class="line">      <span class="comment"># 订单 orders 主库的数据源配置</span></span><br><span class="line">      <span class="attr">ds-master:</span></span><br><span class="line">        <span class="attr">type:</span> <span class="string">com.zaxxer.hikari.HikariDataSource</span> <span class="comment"># 使用 Hikari 数据库连接池</span></span><br><span class="line">        <span class="attr">driver-class-name:</span> <span class="string">com.mysql.cj.jdbc.Driver</span></span><br><span class="line">        <span class="attr">jdbc-url:</span> <span class="string">jdbc:mysql://127.0.0.1:3306/labs-springboot2?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai</span></span><br><span class="line">        <span class="attr">username:</span> <span class="string">root</span></span><br><span class="line">        <span class="attr">password:</span> <span class="string">root</span></span><br><span class="line">      <span class="comment"># 订单 orders 从库数据源配置</span></span><br><span class="line">      <span class="attr">ds-slave-1:</span></span><br><span class="line">        <span class="attr">type:</span> <span class="string">com.zaxxer.hikari.HikariDataSource</span> <span class="comment"># 使用 Hikari 数据库连接池</span></span><br><span class="line">        <span class="attr">driver-class-name:</span> <span class="string">com.mysql.cj.jdbc.Driver</span></span><br><span class="line">        <span class="attr">jdbc-url:</span> <span class="string">jdbc:mysql://127.0.0.1:3306/labs-springboot2-multiple?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai</span></span><br><span class="line">        <span class="attr">username:</span> <span class="string">root</span></span><br><span class="line">        <span class="attr">password:</span> <span class="string">root</span></span><br><span class="line">      <span class="comment"># 订单 orders 从库数据源配置</span></span><br><span class="line">      <span class="attr">ds-slave-2:</span></span><br><span class="line">        <span class="attr">type:</span> <span class="string">com.zaxxer.hikari.HikariDataSource</span> <span class="comment"># 使用 Hikari 数据库连接池</span></span><br><span class="line">        <span class="attr">driver-class-name:</span> <span class="string">com.mysql.cj.jdbc.Driver</span></span><br><span class="line">        <span class="attr">jdbc-url:</span> <span class="string">jdbc:mysql://127.0.0.1:3306/mysql_learn?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai</span></span><br><span class="line">        <span class="attr">username:</span> <span class="string">root</span></span><br><span class="line">        <span class="attr">password:</span> <span class="string">root</span></span><br><span class="line">    <span class="comment"># 读写分离配置，对应 YamlMasterSlaveRuleConfiguration 配置类</span></span><br><span class="line">    <span class="attr">masterslave:</span></span><br><span class="line">      <span class="attr">name:</span> <span class="string">ms</span> <span class="comment"># 名字，任意，需要保证唯一</span></span><br><span class="line">      <span class="attr">master-data-source-name:</span> <span class="string">ds-master</span> <span class="comment"># 主库数据源</span></span><br><span class="line">      <span class="attr">slave-data-source-names:</span> <span class="string">ds-slave-1,</span> <span class="string">ds-slave-2</span> <span class="comment"># 从库数据源</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># mybatis 配置内容</span></span><br><span class="line"><span class="attr">mybatis:</span></span><br><span class="line">  <span class="attr">config-location:</span> <span class="string">classpath:mybatis-config.xml</span> <span class="comment"># 配置 MyBatis 配置文件路径</span></span><br><span class="line">  <span class="attr">mapper-locations:</span> <span class="string">classpath:mapper/*.xml</span> <span class="comment"># 配置 Mapper XML 地址</span></span><br><span class="line">  <span class="attr">type-aliases-package:</span> <span class="string">cn.javadog.labs.springboot2.datasource.dynamic.shardingjdbc.two.dataobject</span> <span class="comment"># 配置数据库实体包路径</span></span><br></pre></td></tr></table></figure><ul><li><code>spring.shardingsphere.datasource</code> 配置项下，我们配置了 一个主数据源 <code>ds-master</code> 、一个从数据源 <code>ds-slave-1</code>、<code>ds-slave-2</code> 。</li><li><code>spring.shardingsphere.masterslave</code> 配置项下，配置了读写分离。对于从库来说，Sharding-JDBC 提供了多种负载均衡策略，默认为轮询。</li><li><code>mybatis</code> 配置项，设置 <code>mybatis-spring-boot-starter</code> MyBatis 的配置内容。</li><li>因为我本地并未搭建 MySQL 一主多从的环境，所以是通过创建了 <code>test_orders_01</code>、<code>test_orders_02</code> 库，手动模拟作为 <code>test_orders</code> 的从库。</li></ul><h2 id="9-4-MyBatis-配置文件"><a href="#9-4-MyBatis-配置文件" class="headerlink" title="9.4 MyBatis 配置文件"></a>9.4 MyBatis 配置文件</h2><p>和 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「3.4 MyBatis 配置文件」</a> 一致。</p><h2 id="9-5-OrderDO"><a href="#9-5-OrderDO" class="headerlink" title="9.5 OrderDO"></a>9.5 OrderDO</h2><p>只使用 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「3.5 实体类」</a> 的 <a href="https://github.com/yuyong725/labs-springboot2/blob/master/labs-springboot2-data-access/labs-springboot2-datasource-dynamic/labs-springboot2-datasource-dynamic-shardingjdbc-02/src/main/java/cn/javadog/labs/springboot2/datasource/dynamic/shardingjdbc/two/dataobject/OrderDO.java" target="_blank" rel="noopener">OrderDO.java</a> 类。</p><h2 id="9-6-OrderMapper"><a href="#9-6-OrderMapper" class="headerlink" title="9.6 OrderMapper"></a>9.6 OrderMapper</h2><p>和 <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/">「4.6 OrderMapper」</a> 基本一致，差别是无需 <code>@DS</code> 注解，具体看如下两个传送门：</p><ul><li><a href="https://github.com/yuyong725/labs-springboot2/blob/master/labs-springboot2-data-access/labs-springboot2-datasource-dynamic/labs-springboot2-datasource-dynamic-shardingjdbc-02/src/main/java/cn/javadog/labs/springboot2/datasource/dynamic/shardingjdbc/two/mapper/OrderMapper.java" target="_blank" rel="noopener">OrderMapper</a></li><li><a href="https://github.com/yuyong725/labs-springboot2/blob/master/labs-springboot2-data-access/labs-springboot2-datasource-dynamic/labs-springboot2-datasource-dynamic-shardingjdbc-02/src/main/resources/mapper/OrderMapper.xml" target="_blank" rel="noopener">OrderMapper.xml</a></li></ul><h2 id="9-7-简单测试"><a href="#9-7-简单测试" class="headerlink" title="9.7 简单测试"></a>9.7 简单测试</h2><p>创建 <code>OrderMapperTest</code> 测试类，我们来测试一下简单的 OrderMapper 的读写操作。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// OrderMapper.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@RunWith</span>(SpringRunner<span class="class">.<span class="keyword">class</span>)</span></span><br><span class="line"><span class="class">@<span class="title">SpringBootTest</span>(<span class="title">classes</span> </span>= Application<span class="class">.<span class="keyword">class</span>)</span></span><br><span class="line"><span class="class"><span class="title">public</span> <span class="title">class</span> <span class="title">OrderMapperTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> OrderMapper orderMapper;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testSelectById</span><span class="params">()</span> </span>&#123; <span class="comment">// 测试从库的负载均衡</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">            OrderDO order = orderMapper.selectById(<span class="number">1</span>);</span><br><span class="line">            System.out.println(order);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testSelectById02</span><span class="params">()</span> </span>&#123; <span class="comment">// 测试强制访问主库</span></span><br><span class="line">        <span class="keyword">try</span> (HintManager hintManager = HintManager.getInstance()) &#123;</span><br><span class="line">            <span class="comment">// 设置强制访问主库</span></span><br><span class="line">            hintManager.setMasterRouteOnly();</span><br><span class="line">            <span class="comment">// 执行查询</span></span><br><span class="line">            OrderDO order = orderMapper.selectById(<span class="number">1</span>);</span><br><span class="line">            System.out.println(order);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testInsert</span><span class="params">()</span> </span>&#123; <span class="comment">// 插入</span></span><br><span class="line">        OrderDO order = <span class="keyword">new</span> OrderDO();</span><br><span class="line">        order.setUserId(<span class="number">10</span>);</span><br><span class="line">        orderMapper.insert(order);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p><code>#testSelectById()</code> 方法，测试从库的负载均衡查询。</p></li><li><p><code>#testSelectById02()</code> 方法，测试强制访问主库。在一些业务场景下，对数据延迟敏感，所以只能强制读取主库。此时，可以使用 <code>HintManager</code> 强制访问主库。</p><ul><li>不过要注意，在使用完后，需要去清理下 HintManager （HintManager 是基于线程变量，透传给 Sharding-JDBC 的内部实现），避免污染下次请求，一直强制访问主库。</li><li>Sharding-JDBC 比较贴心，HintManager 实现了 AutoCloseable 接口，可以通过 <a href="https://www.cnblogs.com/barrywxx/p/9993005.html" target="_blank" rel="noopener">Try-with-resources</a> 机制，自动关闭。</li></ul></li><li><p><code>#testInsert()</code> 方法，测试主库的插入。</p></li></ul><p>胖友自己跑下测试用例。如果跑通，说明配置就算成功了。</p><p>另外，在 <code>#testSelectById()</code> 测试方法中，卡姆为了看看 <code>slave</code> 分组是不是真的在负载均衡。所以在数据库中，分别插入数据如下。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">主库：[id &#x3D; 1, user_id &#x3D; 1]</span><br><span class="line">从库 01：[id &#x3D; 1, user_id &#x3D; 2]</span><br><span class="line">从库 02：[id &#x3D; 1, user_id &#x3D; 3]</span><br></pre></td></tr></table></figure><ul><li>这样，通过手动设置相同 <code>id = 1</code> 的记录，对应不同的 <code>user_id</code> ，那么我们就可以观察 <code>#testSelectById()</code> 测试方法的输出结果。如果是，<code>user_id = 2</code> 和 <code>user_i = 3</code> 循环输出，说明就正常了。</li></ul><h2 id="9-8-详细测试"><a href="#9-8-详细测试" class="headerlink" title="9.8 详细测试"></a>9.8 详细测试</h2><p>在 <code>cn.javadog.labs.springboot2.datasource.dynamic.shardingjdbc.two.service</code> 包路径下，创建 <code>OrderService.java</code> 类。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// OrderService.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">OrderService</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> OrderMapper orderMapper;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Transactional</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">add</span><span class="params">(OrderDO order)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// &lt;1.1&gt; 这里先假模假样的读取一下。读取从库</span></span><br><span class="line">        OrderDO exists = orderMapper.selectById(<span class="number">1</span>);</span><br><span class="line">        System.out.println(exists);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// &lt;1.2&gt; 插入订单</span></span><br><span class="line">        orderMapper.insert(order);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// &lt;1.3&gt; 这里先假模假样的读取一下。读取主库</span></span><br><span class="line">        exists = orderMapper.selectById(<span class="number">1</span>);</span><br><span class="line">        System.out.println(exists);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> OrderDO <span class="title">findById</span><span class="params">(Integer id)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> orderMapper.selectById(id);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>我们创建了 <code>OrderServiceTest</code>测试类，可以测试上面编写的两个方法。</p></li><li><p>在 <code>#add(OrderDO order)</code> 方法中，开启事务，插入一条订单记录。</p><ul><li><code>&lt;1.1&gt;</code> 处，往<strong>从库</strong>发起一次订单查询。在 Sharding-JDBC 的读写分离策略里，默认读取从库。</li></ul></li><li><p><code>&lt;1.2&gt;</code> 处，往<strong>主库</strong>发起一次订单写入。写入，肯定是操作主库的。</p><ul><li><code>&lt;1.3&gt;</code> 处，往<strong>主库</strong>发起一次订单查询。在 Sharding-JDBC 中，读写分离约定：<strong>同一线程且同一数据库连接内，如有写入操作，以后的读操作均从主库读取，用于保证数据一致性。</strong></li></ul></li><li><p>在 <code>#findById(Integer id)</code> 方法，往<strong>从库</strong>发起一次订单查询。</p></li></ul><h1 id="666-彩蛋"><a href="#666-彩蛋" class="headerlink" title="666. 彩蛋"></a>666. 彩蛋</h1><p>我们看完了三种多数据源的方案，实际场景下怎么选择呢？</p><p>首先，我们基本排除了方案二【不同操作类，固定数据源】。配置繁琐，使用不变。「芋道源码」也去问了一圈朋友，暂时没有这么做的。这种方案，更加适合<strong>不同类型</strong>的数据源，例如说一个项目中，既有 MySQL 数据源，又有 MongoDB、Elasticsarch 等其它数据源。</p><p>然后，对于大多数场景下，方案一【基于 SpringAbstractRoutingDataSource 做拓展】，基本能够满足。这种方案，目前是比较主流的方案，大多数项目都采用。在实现上，我们可以比较容易的自己封装一套，当然也可以考虑使用 <a href="https://mp.baomidou.com/guide/dynamic-datasource.html" target="_blank" rel="noopener">dynamic-datasource-spring-boot-starter</a> 开源项目。不过呢，建议可以把它的源码撸一下，核心代码估计 1000 行左右，不要慌。</p><p>当然，方案一和方案二，会存在和 Spring 事务结合的时候，在事务中无法切换数据源。这是因为 Spring 事务会将 Connection 和当前线程变量绑定定，后续会通过线程变量重用该 Connection ，导致无法切换数据源。所以，方案一和方案二，可以理解成 DataSource 级别上实现的数据源方案。</p><blockquote><ul><li>京东：采用 client 模式的读写分离和分库分表。</li><li>美团：采用 client 模式的读写分离和分库分表。</li><li>陌陌：采用 client 模式的读写分离和分库分表。</li></ul><p>… 继续补充调研 ing 。</p></blockquote><p>😝 因为本文写的相对匆忙，如果有表述不正确，或者错误的地方，烦请胖友指出。感谢~</p><p>推荐阅读：</p><ul><li><a href="http://www.iocoder.cn/Spring-Boot/sharding-datasource/?self" target="_blank" rel="noopener">《芋道 Spring Boot 分库分表入门》</a> 对应 <a href="https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-18" target="_blank" rel="noopener">lab-18</a> 。</li><li><a href="http://www.iocoder.cn/Fight/Spring-MyBatis-implements-the-database-read-and-write-separation-scheme/?self" target="_blank" rel="noopener">《Spring + MyBatis 实现数据库读写分离方案》</a></li></ul></div><div class="article-footer"><blockquote class="mt-2x"><ul class="post-copyright list-unstyled"><li class="post-copyright-link hidden-xs"><strong>本文链接：</strong> <a href="https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/" title="Spring Boot 多数据源（读写分离）入门" target="_blank" rel="external">https://calm_java.gitee.io/blog/2020/02/13/labs-springboot2-dynamic-datasource/</a></li><li class="post-copyright-license"><strong>版权声明： </strong>本博客所有文章除特别声明外，均采用 <a href="http://creativecommons.org/licenses/by/4.0/deed.zh" target="_blank" rel="external">CC BY 4.0 CN协议</a> 许可协议。转载请注明出处！</li></ul></blockquote><div class="panel panel-default panel-badger"><div class="panel-body"><figure class="media"><div class="media-left"><a href="https://github.com/yuyong725" target="_blank" class="img-burn thumb-sm visible-lg"><img src="/blog/images/avatar.jpg" class="img-rounded w-full" alt=""></a></div><div class="media-body"><h3 class="media-heading"><a href="https://github.com/yuyong725" target="_blank"><span class="text-dark">卡姆</span><small class="ml-1x">a Java coder need calm</small></a></h3><div>享受编程</div></div></figure></div></div></div></article><section id="comments"></section></div><nav class="bar bar-footer clearfix" data-stick-bottom><div class="bar-inner"><ul class="pager pull-left"><li class="prev"><a href="/blog/2020/02/17/labs-springboot2-sharding-datasource/" title="Spring Boot 分库分表入门"><i class="icon icon-angle-left" aria-hidden="true"></i><span>&nbsp;&nbsp;上一篇</span></a></li><li class="next"><a href="/blog/2020/01/09/labs-springboot2-redis/" title="Spring Boot Redis 入门"><span>下一篇&nbsp;&nbsp;</span><i class="icon icon-angle-right" aria-hidden="true"></i></a></li><li class="toggle-toc"><a class="toggle-btn" data-toggle="collapse" href="#collapseToc" aria-expanded="true" title="文章目录" role="button"><span>[&nbsp;</span><span>文章目录</span> <i class="text-collapsed icon icon-anchor"></i> <i class="text-in icon icon-close"></i> <span>]</span></a></li></ul><button type="button" class="btn btn-fancy btn-donate pop-onhover bg-gradient-warning" data-toggle="modal" data-target="#donateModal"><span>赏</span></button><div class="bar-right"><div class="share-component" data-sites="wechat" data-mobile-sites="wechat"></div></div></div></nav><div class="modal modal-center modal-small modal-xs-full fade" id="donateModal" tabindex="-1" role="dialog"><div class="modal-dialog" role="document"><div class="modal-content donate"><button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button><div class="modal-body"><div class="donate-box"><div class="donate-head"><p>感谢您的支持，我会继续努力的!</p></div><div class="tab-content"><div role="tabpanel" class="tab-pane fade active in" id="alipay"><div class="donate-payimg"><img src="/blog/images/donate/alipayimg.jpeg" alt="扫码支持" title="扫一扫"></div><p class="text-muted mv">扫码打赏，你说多少就多少</p><p class="text-grey">打开支付宝扫一扫，即可进行扫码打赏哦</p></div><div role="tabpanel" class="tab-pane fade" id="wechatpay"><div class="donate-payimg"><img src="/blog/images/donate/wechatpayimg.jpeg" alt="扫码支持" title="扫一扫"></div><p class="text-muted mv">扫码打赏，你说多少就多少</p><p class="text-grey">打开微信扫一扫，即可进行扫码打赏哦</p></div></div><div class="donate-footer"><ul class="nav nav-tabs nav-justified" role="tablist"><li role="presentation" class="active"><a href="#alipay" id="alipay-tab" role="tab" data-toggle="tab" aria-controls="alipay" aria-expanded="true"><i class="icon icon-alipay"></i> 支付宝</a></li><li role="presentation"><a href="#wechatpay" role="tab" id="wechatpay-tab" data-toggle="tab" aria-controls="wechatpay" aria-expanded="false"><i class="icon icon-wepay"></i> 微信支付</a></li></ul></div></div></div></div></div></div></main><footer class="footer" itemscope itemtype="http://schema.org/WPFooter"></footer><script src="//cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"></script><script>window.jQuery||document.write('<script src="js/jquery.min.js"><\/script>')</script><script src="/blog/js/plugin.min.js"></script><script src="/blog/js/application.js"></script><script>!function(T){var N={TRANSLATION:{POSTS:"文章",PAGES:"页面",CATEGORIES:"分类",TAGS:"标签",UNTITLED:"(未命名)"},ROOT_URL:"/blog/",CONTENT_URL:"/blog/content.json"};T.INSIGHT_CONFIG=N}(window)</script><script src="/blog/js/insight.js"></script><script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script><script src="//cdn.jsdelivr.net/npm/gitalk@1.4.0/dist/gitalk.min.js"></script><script src="//cdn.jsdelivr.net/npm/blueimp-md5@2.10.0/js/md5.min.js"></script><script type="text/javascript">var gitalk=new Gitalk({clientID:"adf0c8bfdd8796029917",clientSecret:"a88c2e2e83e2b180f62ad927dcb347871dfe9f4b",repo:"myblog-comment",owner:"yuyong725",admin:["yuyong725"],id:md5(location.pathname),distractionFreeMode:!0});gitalk.render("comments")</script></body></html><!-- rebuild by neat -->